summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUdo Walter2018-08-02 18:13:05 +0200
committerUdo Walter2018-08-02 18:13:05 +0200
commit028c9aaa8651862b2bd495c527d30845d1fb1f50 (patch)
tree2b6bc68432940ebfe7078a8aefad26f369c09f08
parent[groups] small bugfix (diff)
downloadbas-028c9aaa8651862b2bd495c527d30845d1fb1f50.tar.gz
bas-028c9aaa8651862b2bd495c527d30845d1fb1f50.tar.xz
bas-028c9aaa8651862b2bd495c527d30845d1fb1f50.zip
[groups] add dialog to delete, remove and add groups/clients
-rw-r--r--server/api/clients.js4
-rw-r--r--server/api/groups.js42
-rw-r--r--webapp/src/components/GroupModuleClientList.vue16
-rw-r--r--webapp/src/components/GroupModuleClientView.vue20
-rw-r--r--webapp/src/components/GroupModuleDialog.vue137
-rw-r--r--webapp/src/components/GroupModuleGroupList.vue16
-rw-r--r--webapp/src/components/GroupModuleGroupView.vue22
-rw-r--r--webapp/src/main.js10
-rw-r--r--webapp/src/store/groups.js58
9 files changed, 274 insertions, 51 deletions
diff --git a/server/api/clients.js b/server/api/clients.js
index ef0dcaa..4f7ce1d 100644
--- a/server/api/clients.js
+++ b/server/api/clients.js
@@ -38,11 +38,11 @@ module.exports.post = {
save: function (req, res) {
const id = req.body.id > 0 ? req.body.id : null
if (id) {
- db.client.findOne({ where: { id: id } }).then(client => {
+ db.client.findOne({ where: { id } }).then(client => {
var promises = []
if (req.body.info) promises.push([client.update(req.body.info)])
if (req.body.groupIds) promises.push(client.setGroups(req.body.groupIds))
- Promise.all(promises).then(() => { res.send({id}) })
+ Promise.all(promises).then(() => { res.send({ id }) })
})
} else {
db.client.create(req.body.info).then(client => {
diff --git a/server/api/groups.js b/server/api/groups.js
index ece6b43..a41666a 100644
--- a/server/api/groups.js
+++ b/server/api/groups.js
@@ -39,7 +39,7 @@ module.exports.post = {
save: function (req, res) {
const id = req.body.id > 0 ? req.body.id : null
if (id) {
- db.group.findOne({ where: { id: id } }).then(group => {
+ db.group.findOne({ where: { id } }).then(group => {
var promises = []
if (req.body.info) promises.push([group.update(req.body.info)])
if (req.body.parentIds) promises.push(group.setParents(req.body.parentIds))
@@ -55,5 +55,45 @@ module.exports.post = {
// delete groups
delete: function (req, res) {
db.group.destroy({ where: { id: req.body.ids } }).then(() => { res.end() })
+ },
+
+ // remove subgroups from a group
+ removeSubgroups: function (req, res) {
+ const id = req.body.id > 0 ? req.body.id : null
+ if (id) {
+ db.group.findOne({ where: { id } }).then(group => {
+ group.removeSubgroups(req.body.ids).then(() => { res.end() })
+ })
+ } else { res.status(404).end() }
+ },
+
+ // remove clients from a group
+ removeClients: function (req, res) {
+ const id = req.body.id > 0 ? req.body.id : null
+ if (id) {
+ db.group.findOne({ where: { id } }).then(group => {
+ group.removeClients(req.body.ids).then(() => { res.end() })
+ })
+ } else { res.status(404).end() }
+ },
+
+ // add subgroups to a group
+ addSubgroups: function (req, res) {
+ const id = req.body.id > 0 ? req.body.id : null
+ if (id) {
+ db.group.findOne({ where: { id } }).then(group => {
+ group.addSubgroups(req.body.ids).then(() => { res.end() })
+ })
+ } else { res.status(404).end() }
+ },
+
+ // add clients to a group
+ addClients: function (req, res) {
+ const id = req.body.id > 0 ? req.body.id : null
+ if (id) {
+ db.group.findOne({ where: { id } }).then(group => {
+ group.addClients(req.body.ids).then(() => { res.end() })
+ })
+ } else { res.status(404).end() }
}
}
diff --git a/webapp/src/components/GroupModuleClientList.vue b/webapp/src/components/GroupModuleClientList.vue
index ec1a774..0b60273 100644
--- a/webapp/src/components/GroupModuleClientList.vue
+++ b/webapp/src/components/GroupModuleClientList.vue
@@ -27,7 +27,7 @@
>
<template slot="items" slot-scope="props">
<tr @click="loadClient(props.item.id)">
- <td class="narrow-td">
+ <td class="narrow-td" @click.stop="props.selected = !props.selected">
<v-checkbox
color="primary"
v-model="props.selected"
@@ -52,8 +52,8 @@
<v-btn flat color="success" @click="newClient"><v-icon left>create</v-icon>Create client</v-btn>
</div>
<div v-else class="text-xs-right">
- <v-btn flat color="error"><v-icon left>remove_circle_outline</v-icon>Remove selected clients</v-btn>
- <v-btn flat color="success"><v-icon left>add_circle_outline</v-icon>Add clients</v-btn>
+ <v-btn flat color="error" @click="removeSelected" :disabled="selected.length === 0"><v-icon left>remove_circle_outline</v-icon>Remove selected clients</v-btn>
+ <v-btn flat color="success" @click="addExisting"><v-icon left>add_circle_outline</v-icon>Add clients</v-btn>
</div>
</div>
</template>
@@ -63,7 +63,7 @@ import { mapState, mapMutations } from 'vuex'
export default {
name: 'GroupModuleClientList',
- props: ['tabIndex', 'clients'],
+ props: ['tabIndex', 'groupId', 'clients'],
data () {
return {
headers: [
@@ -99,7 +99,13 @@ export default {
this.setActiveTab(1)
},
deleteSelected () {
- this.setDialog({ show: true, type: 'deleteClients', selected: this.selected })
+ this.setDialog({ show: true, info: { action: 'delete', type: 'client', selected: this.selected } })
+ },
+ removeSelected () {
+ this.setDialog({ show: true, info: { action: 'remove', type: 'client', selected: this.selected, tabIndex: this.tabIndex } })
+ },
+ addExisting () {
+ this.setDialog({ show: true, info: { action: 'add', type: 'client', selected: this.selected, tabIndex: this.tabIndex } })
}
}
}
diff --git a/webapp/src/components/GroupModuleClientView.vue b/webapp/src/components/GroupModuleClientView.vue
index e2c381b..50d5507 100644
--- a/webapp/src/components/GroupModuleClientView.vue
+++ b/webapp/src/components/GroupModuleClientView.vue
@@ -51,15 +51,22 @@
v-model="groupIds"
hide-details
offset-y
- label="Parents"
+ label="Groups"
color="primary"
multiple
+ item-value="id"
>
<template slot="selection" slot-scope="data">
- <v-chip :color="chipColor" :text-color="chipTextColor" small :selected="data.selected" @input="removeGroup(data.item.value)" close>
- {{ data.item.text }}
+ <v-chip :color="chipColor" :text-color="chipTextColor" small :selected="data.selected" @input="removeGroup(data.item.id)" close>
+ {{ data.item.name || data.item.id }}
</v-chip>
</template>
+ <template slot="item" slot-scope="data">
+ <div class="select-item">
+ <v-checkbox class="select-item-checkbox" color="primary" :value="groupIds.includes(data.item.id)" hide-details></v-checkbox>
+ {{ data.item.name || data.item.id }}
+ </div>
+ </template>
</v-autocomplete>
<div v-else class="info-input">
<div class="body-2">Groups</div>
@@ -170,4 +177,11 @@ export default {
.info-input {
margin: 10px;
}
+.select-item {
+ display: flex;
+ align-items: center;
+}
+.select-item-checkbox {
+ margin-right: 20px;
+}
</style>
diff --git a/webapp/src/components/GroupModuleDialog.vue b/webapp/src/components/GroupModuleDialog.vue
index 43cec3e..720913f 100644
--- a/webapp/src/components/GroupModuleDialog.vue
+++ b/webapp/src/components/GroupModuleDialog.vue
@@ -1,37 +1,85 @@
<i18n>
{
"en": {
+ "title-delete-group": "Delete this group? | Delete these {0} groups?",
+ "title-delete-client": "Delete this client? | Delete these {0} clients?",
+ "title-remove-group": "Remove this subgroup? | Remove these {0} subgroups?",
+ "title-remove-client": "Remove this client? | Remove these {0} clients?",
+ "title-add-group": "Add groups | Add this group? | Add these {0} groups?",
+ "title-add-client": "Add clients | Add this client? | Add these {0} clients?",
+ "new": "New"
},
"de": {
+ "title-delete-group": "Diese Gruppe löschen? | Diese {0} Gruppen löschen?",
+ "title-delete-client": "Diesen Clienten löschen? | Diese {0} Clienten löschen?",
+ "title-remove-group": "Diese Untergruppe entfernen? | Diese {0} Untergruppen entfernen?",
+ "title-remove-client": "Diesen Clienten entfernen? | Diese {0} Clienten entfernen?",
+ "title-add-group": "Gruppen hinzufügen | Diese Gruppe hinzufügen? | Diese {0} Gruppen hinzufügen?",
+ "title-add-client": "Clienten hinzufügen | Diesen Clienten hinzufügen? | Diese {0} Clienten hinzufügen?",
+ "delete-instead-remove": "Gruppen komplett löschen",
+ "new": "Neu"
}
}
</i18n>
<template>
<v-dialog
- v-if="dialog.type === 'deleteGroups' || dialog.type === 'deleteClients'"
:value="dialog.show"
@input="setDialog({ show: $event })"
max-width="500px"
scrollable
>
<v-card>
- <v-card-title primary-title class="elevation-3">
- <div>
- <div class="headline">Delete selected?</div>
- </div>
+ <v-card-title primary-title class="dialog-title elevation-3">
+ <div class="headline">{{ title }}</div>
</v-card-title>
- <v-card-text>
- Are you sure?
- <div style="margin: 10px">
- <div v-for="item in dialog.selected" class="grey--text" :key="item.id">[{{ item.id }}] {{ item.name }}</div>
- </div>
+ <v-card-text v-if="dialog.info.action === 'add'" class="table-container">
+ <v-layout justify-space-between align-center>
+ <v-btn flat color="success" @click="newItem"><v-icon left>create</v-icon>{{ $t('new') }}</v-btn>
+ <v-text-field class="search-field" v-model="search" hide-details prepend-inner-icon="search"></v-text-field>
+ </v-layout>
+ <v-divider></v-divider>
+ <v-data-table
+ :headers="headers"
+ :items="items"
+ item-key="id"
+ select-all
+ hide-actions
+ v-model="selected"
+ :search="search"
+ >
+ <template slot="items" slot-scope="props">
+ <tr @click="props.selected = !props.selected">
+ <td>
+ <v-checkbox
+ color="primary"
+ v-model="props.selected"
+ hide-details
+ @click.native.stop
+ ></v-checkbox>
+ </td>
+ <td>{{ props.item.id }}</td>
+ <td>{{ props.item.name }}</td>
+ </tr>
+ </template>
+ </v-data-table>
+ </v-card-text>
+ <v-card-text v-else style="margin: 10px">
+ <v-checkbox
+ class="delete-checkbox"
+ v-if="dialog.info.action === 'remove'"
+ :label="$t('delete-instead-remove')"
+ color="error"
+ v-model="deleteInsteadOfRemove"
+ hide-details
+ ></v-checkbox>
+ <div v-for="item in dialog.info.selected" class="grey--text" :key="item.id">[{{ item.id }}] {{ item.name }}</div>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn flat="flat" @click="setDialog({ show: false })">{{ $t('cancel') }}</v-btn>
- <v-btn color="error" @click="deleteSelected">{{ $t('delete') }}</v-btn>
+ <v-btn :color="dialog.info.action === 'add' ? 'success' : 'error'" @click="submitAction">{{ $t(action) }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@@ -44,17 +92,57 @@ export default {
name: 'GroupModuleDialog',
data () {
return {
+ headers: [
+ { text: 'ID', value: 'id' },
+ { text: 'Name', value: 'name', width: '100000px' }
+ ],
+ selected: [],
+ search: '',
+ deleteInsteadOfRemove: false
}
},
computed: {
- ...mapState('groups', ['dialog'])
+ ...mapState('groups', ['dialog', 'tabChain']),
+ action () {
+ return this.dialog.info.action === 'remove' && this.deleteInsteadOfRemove ? 'delete' : this.dialog.info.action
+ },
+ title () {
+ const count = this.dialog.info.action === 'add' ? this.selected.length : this.dialog.info.selected.length
+ return this.$tc('title-' + this.action + '-' + this.dialog.info.type, count, [count])
+ },
+ items () {
+ if (this.dialog.info.type === 'group') return this.$store.state.groups.groupList
+ if (this.dialog.info.type === 'client') return this.$store.state.groups.clientList
+ }
},
methods: {
- ...mapMutations('groups', ['setDialog']),
- deleteSelected () {
- if (this.dialog.type === 'deleteGroups') this.$store.dispatch('groups/deleteGroups', this.dialog.selected.map(x => x.id))
- if (this.dialog.type === 'deleteClients') this.$store.dispatch('groups/deleteClients', this.dialog.selected.map(x => x.id))
+ ...mapMutations('groups', ['setActiveTab', 'setTab']),
+ setDialog (data) {
+ this.$store.commit('groups/setDialog', data)
+ if (data.show === false) {
+ this.selected = []
+ this.search = ''
+ this.deleteInsteadOfRemove = false
+ }
+ },
+ submitAction () {
+ const actionMap = {
+ 'delete': { 'group': 'deleteGroups', 'client': 'deleteClients'},
+ 'remove': { 'group': 'removeSubroups', 'client': 'removeClients'},
+ 'add': { 'group': 'addSubgroups', 'client': 'addClients'}
+ }
+ var data = { ...this.dialog.info }
+ if (this.dialog.info.action === 'add') data.selected = this.selected
+ this.$store.dispatch('groups/' + actionMap[this.dialog.info.action][this.dialog.info.type], data)
+ this.setDialog({ show: false })
+ },
+ newItem () {
this.setDialog({ show: false })
+ var item = { tabType: this.dialog.info.type }
+ if (this.dialog.info.type === 'group') item.parents = [this.tabChain[this.dialog.info.tabIndex]]
+ else if (this.dialog.info.type === 'client') item.groups = [this.tabChain[this.dialog.info.tabIndex]]
+ this.setTab({ index: this.dialog.info.tabIndex + 1, item })
+ this.setActiveTab(this.dialog.info.tabIndex + 1)
}
}
}
@@ -62,9 +150,18 @@ export default {
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
-.show-toggle {
- margin-top: 20px;
- margin-right: 20px;
- margin-bottom: -20px;
+.table-container {
+ padding: 0;
+}
+.search-field {
+ margin: 10px;
+ max-width: 200px;
+}
+.dialog-title {
+ z-index: 1;
+}
+.delete-checkbox {
+ margin-top: 0;
+ margin-bottom: 20px;
}
</style>
diff --git a/webapp/src/components/GroupModuleGroupList.vue b/webapp/src/components/GroupModuleGroupList.vue
index 62d447c..7d559b6 100644
--- a/webapp/src/components/GroupModuleGroupList.vue
+++ b/webapp/src/components/GroupModuleGroupList.vue
@@ -27,7 +27,7 @@
>
<template slot="items" slot-scope="props">
<tr @click="loadGroup(props.item.id)">
- <td>
+ <td @click.stop="props.selected = !props.selected">
<v-checkbox
color="primary"
v-model="props.selected"
@@ -50,8 +50,8 @@
<v-btn flat color="success" @click="newGroup"><v-icon left>create</v-icon>Create group</v-btn>
</div>
<div v-else class="text-xs-right">
- <v-btn flat color="error"><v-icon left>remove_circle_outline</v-icon>Remove selected subgroups</v-btn>
- <v-btn flat color="success"><v-icon left>add_circle_outline</v-icon>Add subgroups</v-btn>
+ <v-btn flat color="error" @click="removeSelected" :disabled="selected.length === 0"><v-icon left>remove_circle_outline</v-icon>Remove selected subgroups</v-btn>
+ <v-btn flat color="success" @click="addExisting"><v-icon left>add_circle_outline</v-icon>Add subgroups</v-btn>
</div>
</div>
</template>
@@ -61,7 +61,7 @@ import { mapState, mapMutations } from 'vuex'
export default {
name: 'GroupModuleGroupList',
- props: ['tabIndex', 'groups'],
+ props: ['tabIndex', 'groupId', 'groups'],
data () {
return {
headers: [
@@ -95,7 +95,13 @@ export default {
this.setActiveTab(1)
},
deleteSelected () {
- this.setDialog({ show: true, type: 'deleteGroups', selected: this.selected })
+ this.setDialog({ show: true, info: { action: 'delete', type: 'group', selected: this.selected } })
+ },
+ removeSelected () {
+ this.setDialog({ show: true, info: { action: 'remove', type: 'group', selected: this.selected, tabIndex: this.tabIndex } })
+ },
+ addExisting () {
+ this.setDialog({ show: true, info: { action: 'add', type: 'group', selected: this.selected, tabIndex: this.tabIndex } })
}
}
}
diff --git a/webapp/src/components/GroupModuleGroupView.vue b/webapp/src/components/GroupModuleGroupView.vue
index aac4b38..f20aff7 100644
--- a/webapp/src/components/GroupModuleGroupView.vue
+++ b/webapp/src/components/GroupModuleGroupView.vue
@@ -54,12 +54,19 @@
label="Parents"
color="primary"
multiple
+ item-value="id"
>
<template slot="selection" slot-scope="data">
- <v-chip :color="chipColor" :text-color="chipTextColor" small :selected="data.selected" @input="removeParent(data.item.value)" close>
- {{ data.item.text }}
+ <v-chip :color="chipColor" :text-color="chipTextColor" small :selected="data.selected" @input="removeParent(data.item.id)" close>
+ {{ data.item.name || data.item.id }}
</v-chip>
</template>
+ <template slot="item" slot-scope="data">
+ <div class="select-item">
+ <v-checkbox class="select-item-checkbox" color="primary" :value="parentIds.includes(data.item.id)" hide-details></v-checkbox>
+ {{ data.item.name || data.item.id }}
+ </div>
+ </template>
</v-autocomplete>
<div v-else class="info-input">
<div class="body-2">Parents</div>
@@ -76,9 +83,9 @@
</v-card>
<template v-if="group.id">
<v-subheader>{{ tabIndex === 0 ? 'Groups' : 'Subgoups' }}</v-subheader>
- <group-module-group-list :tabIndex="tabIndex" :groups="group.subgroups" />
+ <group-module-group-list :tabIndex="tabIndex" :groupId="group.id" :groups="group.subgroups" />
<v-subheader>Clients</v-subheader>
- <group-module-client-list :tabIndex="tabIndex" :clients="group.clients" />
+ <group-module-client-list :tabIndex="tabIndex" :groupId="group.id" :clients="group.clients" />
</template>
</div>
</template>
@@ -155,4 +162,11 @@ export default {
.info-input {
margin: 10px;
}
+.select-item {
+ display: flex;
+ align-items: center;
+}
+.select-item-checkbox {
+ margin-right: 20px;
+}
</style>
diff --git a/webapp/src/main.js b/webapp/src/main.js
index 3c4ce47..bfedf3d 100644
--- a/webapp/src/main.js
+++ b/webapp/src/main.js
@@ -27,12 +27,18 @@ const i18n = new VueI18n({
'en': {
'continue': 'Continue',
'cancel': 'Cancel',
- 'delete': 'Delete'
+ 'delete': 'Delete',
+ 'create': 'Create',
+ 'remove': 'Remove',
+ 'add': 'Add'
},
'de': {
'continue': 'Weiter',
'cancel': 'Abbrechen',
- 'delete': 'Löschen'
+ 'delete': 'Löschen',
+ 'create': 'Erstellen',
+ 'remove': 'Entfernen',
+ 'add': 'Hinzufügen'
}
}
})
diff --git a/webapp/src/store/groups.js b/webapp/src/store/groups.js
index 58d64b3..a733aac 100644
--- a/webapp/src/store/groups.js
+++ b/webapp/src/store/groups.js
@@ -10,8 +10,7 @@ export default {
showAll: false,
dialog: {
show: false,
- type: '',
- selected: []
+ info: {}
}
},
mutations: {
@@ -26,17 +25,16 @@ export default {
}
state.tabChain.splice(index, 1, item)
},
- setDialog (state, { show, type, selected }) {
- if (show !== undefined) state.dialog.type = type
- if (selected !== undefined) state.dialog.selected = selected
+ setDialog (state, { show, info, selected }) {
+ if (info !== undefined) state.dialog.info = info
if (show !== undefined) state.dialog.show = show
}
},
actions: {
loadLists (context) {
Promise.all([axios.get('/api/groups/getList'), axios.get('/api/clients/getList')]).then(res => {
- context.commit('setGroupList', res[0].data.map(x => ({ value: x.id, text: x.name || x.id })))
- context.commit('setClientList', res[1].data.map(x => ({ value: x.id, text: x.name || x.id })))
+ context.commit('setGroupList', res[0].data)
+ context.commit('setClientList', res[1].data)
})
},
loadHome (context) {
@@ -95,25 +93,67 @@ export default {
context.dispatch('reload')
})
},
- deleteGroups (context, ids) {
+ deleteGroups (context, { selected }) {
+ const ids = selected.map(x => x.id)
axios.post('/api/groups/delete', { ids }).then(() => {
var i = 1
while (i < context.state.tabChain.length) {
if (context.state.tabChain[i].tabType === 'group' && ids.includes(context.state.tabChain[i].id)) {
context.commit('deleteFromTabChain', { index: i, count: context.state.tabChain.length - i })
+ break
}
i++
}
context.dispatch('reload')
})
},
- deleteClients (context, ids) {
+ deleteClients (context, { selected }) {
+ const ids = selected.map(x => x.id)
axios.post('/api/clients/delete', { ids }).then(() => {
const index = context.state.tabChain.length - 1
const item = context.state.tabChain[index]
if (item.tabType === 'client' && ids.includes(item.id)) context.commit('deleteFromTabChain', { index, count: 1 })
context.dispatch('reload')
})
+ },
+ removeSubroups (context, { tabIndex, selected }) {
+ const id = context.state.tabChain[tabIndex].id
+ const ids = selected.map(x => x.id)
+ axios.post('/api/groups/removeSubgroups', { id, ids }).then(() => {
+ if (context.state.tabChain.length > tabIndex + 1
+ && context.state.tabChain[tabIndex + 1].tabType === 'group'
+ && ids.includes(context.state.tabChain[tabIndex + 1].id)) {
+ context.commit('deleteFromTabChain', { index: tabIndex + 1, count: context.state.tabChain.length - (tabIndex + 1) })
+ }
+ context.dispatch('reload')
+ })
+ },
+ removeClients (context, { tabIndex, selected }) {
+ const id = context.state.tabChain[tabIndex].id
+ const ids = selected.map(x => x.id)
+ axios.post('/api/groups/removeClients', { id, ids }).then(() => {
+ if (context.state.tabChain.length > tabIndex + 1
+ && context.state.tabChain[tabIndex + 1].tabType === 'client'
+ && ids.includes(context.state.tabChain[tabIndex + 1].id)) {
+ context.commit('deleteFromTabChain', { index: tabIndex + 1, count: 1 })
+ }
+ context.dispatch('reload')
+ })
+ },
+ addSubgroups (context, { tabIndex, selected }) {
+ const id = context.state.tabChain[tabIndex].id
+ const ids = selected.map(x => x.id)
+ console.log(ids)
+ axios.post('/api/groups/addSubgroups', { id, ids }).then(() => {
+ context.dispatch('reload')
+ })
+ },
+ addClients (context, { tabIndex, selected }) {
+ const id = context.state.tabChain[tabIndex].id
+ const ids = selected.map(x => x.id)
+ axios.post('/api/groups/addClients', { id, ids }).then(() => {
+ context.dispatch('reload')
+ })
}
}
}