summaryrefslogtreecommitdiffstats
path: root/webapp/src
diff options
context:
space:
mode:
authorUdo Walter2018-11-12 05:59:16 +0100
committerUdo Walter2018-11-12 05:59:16 +0100
commit5a15373dcd76ed7371dbbee88104dd0c4384a889 (patch)
tree1d2c798f7602e17c007d67d279a7cb1ee754b053 /webapp/src
parent[iDoIT] Rework the import method to use batch requests (diff)
downloadbas-5a15373dcd76ed7371dbbee88104dd0c4384a889.tar.gz
bas-5a15373dcd76ed7371dbbee88104dd0c4384a889.tar.xz
bas-5a15373dcd76ed7371dbbee88104dd0c4384a889.zip
[registration] add configurator for registration hooks
Diffstat (limited to 'webapp/src')
-rw-r--r--webapp/src/components/RegistrationModule.vue137
-rw-r--r--webapp/src/components/RegistrationModuleDelete.vue63
-rw-r--r--webapp/src/components/RegistrationModuleEdit.vue120
-rw-r--r--webapp/src/config/dashboard.js2
-rw-r--r--webapp/src/config/i18n.js2
-rw-r--r--webapp/src/config/store.js2
-rw-r--r--webapp/src/store/registration.js40
7 files changed, 366 insertions, 0 deletions
diff --git a/webapp/src/components/RegistrationModule.vue b/webapp/src/components/RegistrationModule.vue
new file mode 100644
index 0000000..f9b857f
--- /dev/null
+++ b/webapp/src/components/RegistrationModule.vue
@@ -0,0 +1,137 @@
+<i18n>
+{
+ "en": {
+ "hooks": "Registration hooks",
+ "createHook": "Create hook"
+ },
+ "de": {
+ "hooks": "Registrierungs Hooks ",
+ "createHook": "Hook erstellen"
+ }
+}
+</i18n>
+
+<template>
+ <v-container fill-height>
+ <v-layout>
+ <v-flex class="tabs-wrapper" xl10 offset-xl1 lg12>
+ <v-card>
+ <v-tabs v-model="tabs" centered :dark="tabsDark" :color="tabsColor" :slider-color="tabsSliderColor">
+ <v-tab>{{ $t('hooks') }}</v-tab>
+ </v-tabs>
+ </v-card>
+ <v-tabs-items v-model="tabs" style="padding-bottom: 20px">
+ <v-tab-item>
+ <v-subheader>{{ $t('hooks') }}</v-subheader>
+ <v-card v-if="hooks.length > 0">
+ <v-list two-line>
+ <draggable :value="hooks" @input="setHooks($event)" :options="{ handle:'.handle' }">
+ <v-list-tile v-for="hook in hooks" :key="hook.id" @click.stop @dblclick="editHook(hook)">
+ <v-list-tile-action class="handle">
+ <v-icon>drag_handle</v-icon>
+ </v-list-tile-action>
+ <v-list-tile-content>
+ <v-list-tile-title>{{ hook.name }}<small class="type">{{ hook.type }}</small></v-list-tile-title>
+ <v-list-tile-sub-title>{{ hook.description }}</v-list-tile-sub-title>
+ </v-list-tile-content>
+ <v-icon v-if="hook.groups.length > 0">device_hub</v-icon>
+ <v-list-tile-action>
+ <v-btn @click="editHook(hook)" icon><v-icon color="primary">edit</v-icon></v-btn>
+ </v-list-tile-action>
+ <v-list-tile-action class="delete">
+ <v-btn @click="deleteHook(hook)" icon><v-icon color="error">delete</v-icon></v-btn>
+ </v-list-tile-action>
+ </v-list-tile>
+ </draggable>
+ </v-list>
+ </v-card>
+ <div class="text-xs-right">
+ <v-btn flat color="success" @click="createHook"><v-icon left>create</v-icon>{{ $t('createHook') }}</v-btn>
+ </div>
+ </v-tab-item>
+ </v-tabs-items>
+ </v-flex>
+ </v-layout>
+ <v-dialog
+ :value="dialog.show"
+ @input="setDialog({ show: $event })"
+ :max-width="dialog.type === 'delete' ? '500px' : '1000px'"
+ scrollable
+ :persistent="dialog.type !== 'delete'"
+ >
+ <registration-module-delete v-if="dialog.type === 'delete'" />
+ <registration-module-edit v-else-if="dialog.type === 'edit'"/>
+ </v-dialog>
+ </v-container>
+</template>
+
+<script>
+import draggable from 'vuedraggable'
+import RegistrationModuleDelete from '@/components/RegistrationModuleDelete'
+import RegistrationModuleEdit from '@/components/RegistrationModuleEdit'
+import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
+
+export default {
+ name: 'RegistrationModule',
+ components: {
+ draggable,
+ RegistrationModuleDelete,
+ RegistrationModuleEdit
+ },
+ data () {
+ return {
+ tabs: 0
+ }
+ },
+ computed: {
+ ...mapGetters(['tabsDark', 'tabsColor', 'tabsSliderColor']),
+ ...mapState('registration', ['hooks', 'dialog'])
+ },
+ methods: {
+ ...mapMutations('registration', ['setDialog']),
+ ...mapActions('registration', ['setHooks']),
+ deleteHook (hook) {
+ this.setDialog({ show: true, type: 'delete', info: hook })
+ },
+ editHook (hook) {
+ this.setDialog({ show: true, type: 'edit', info: hook })
+ },
+ createHook () {
+ this.setDialog({ show: true, type: 'edit', info: {} })
+ }
+ },
+ created () {
+ this.$store.dispatch('registration/loadGroupList')
+ this.$store.dispatch('registration/loadHooks')
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+.handle {
+ margin-left: 12px;
+}
+.delete {
+ margin-right: 12px;
+}
+.type {
+ margin-left: 24px;
+}
+.hook-info {
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+}
+.hook-info > .hook-type {
+ min-width: 60px;
+}
+.hook-info > .hook-name {
+ white-space: nowrap;
+}
+.hook-info > .hook-description {
+ margin-left: 40px;
+ overflow: hidden;
+ white-space: nowrap;
+}
+</style>
diff --git a/webapp/src/components/RegistrationModuleDelete.vue b/webapp/src/components/RegistrationModuleDelete.vue
new file mode 100644
index 0000000..002e629
--- /dev/null
+++ b/webapp/src/components/RegistrationModuleDelete.vue
@@ -0,0 +1,63 @@
+<i18n>
+{
+ "en": {
+ "title": "Delete this hook?"
+ },
+ "de": {
+ "title": "Diesen Hook löschen?"
+ }
+}
+</i18n>
+
+<template>
+ <v-card>
+ <v-card-title primary-title class="dialog-title elevation-3">
+ <div class="headline">{{ $t('title') }}</div>
+ </v-card-title>
+ <v-card-text>
+ {{ dialog.info.type }} {{ dialog.info.name }}
+ </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="deleteItems">{{ $t('delete') }}</v-btn>
+ </v-card-actions>
+ </v-card>
+</template>
+
+<script>
+import axios from 'axios'
+import { mapState } from 'vuex'
+
+export default {
+ name: 'RegistrationModuleDelete',
+ data () {
+ return {
+ }
+ },
+ computed: {
+ ...mapState('registration', ['dialog'])
+ },
+ methods: {
+ setDialog (data) {
+ this.$store.commit('registration/setDialog', data)
+ },
+ async deleteItems () {
+ await axios.delete('/api/registrations/hooks/' + this.dialog.info.id)
+ this.$store.dispatch('registration/loadHooks')
+ this.setDialog({ show: false })
+ }
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+.dialog-title {
+ z-index: 1;
+}
+.selected-list {
+ padding: 30px;
+}
+</style>
diff --git a/webapp/src/components/RegistrationModuleEdit.vue b/webapp/src/components/RegistrationModuleEdit.vue
new file mode 100644
index 0000000..cc4189d
--- /dev/null
+++ b/webapp/src/components/RegistrationModuleEdit.vue
@@ -0,0 +1,120 @@
+<i18n>
+{
+ "en": {
+ "name": "Name",
+ "description": "Description",
+ "type": "Script type",
+ "groups": "Groups",
+ "script": "Script",
+ "titleNew": "Create hook",
+ "titleExisting": "Edit hook"
+ },
+ "de": {
+ "name": "Name",
+ "description": "Beschreibung",
+ "type": "Skript Art",
+ "groups": "Gruppen",
+ "script": "Script",
+ "titleNew": "Hook erstellen",
+ "titleExisting": "Hook bearbeiten"
+ }
+}
+</i18n>
+
+<template>
+ <v-card>
+ <v-card-title primary-title class="dialog-title elevation-3">
+ <div class="headline">{{ dialog.info.id ? $t('titleExisting') : $t('titleNew') }}</div>
+ </v-card-title>
+ <v-card-text>
+ <v-layout row wrap>
+ <v-flex xs12 sm9>
+ <v-text-field prepend-icon="label" :label="$t('name')" color="primary" v-model="name"></v-text-field>
+ </v-flex>
+ <v-flex xs12 sm2 offset-sm1>
+ <v-select prepend-icon="label" color="primary" :items="types" :label="$t('type')" v-model="type" menu-props="offsetY"></v-select>
+ </v-flex>
+ </v-layout>
+ <v-autocomplete
+ prepend-icon="device_hub"
+ :items="groupList"
+ v-model="groups"
+ :label="$t('groups')"
+ color="primary"
+ multiple
+ item-value="id"
+ item-text="name"
+ small-chips
+ deletable-chips
+ ></v-autocomplete>
+ <v-textarea prepend-icon="description" rows="3" :label="$t('description')" color="primary" v-model="description"></v-textarea>
+ <v-textarea prepend-icon="code" rows="20" :label="$t('script')" color="primary" v-model="script"></v-textarea>
+ </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="dialog.info.id ? 'primary' : 'success'" @click="saveHook">{{ dialog.info.id ? $t('save') : $t('create') }}</v-btn>
+ </v-card-actions>
+ </v-card>
+</template>
+
+<script>
+import axios from 'axios'
+import { mapState } from 'vuex'
+
+export default {
+ name: 'RegistrationModuleEdit',
+ data () {
+ return {
+ name: '',
+ description: '',
+ type: '',
+ groups: [],
+ script: '',
+ types: ['BASH', 'IPXE']
+ }
+ },
+ computed: {
+ ...mapState('registration', ['dialog', 'groupList'])
+ },
+ watch: {
+ dialog: {
+ immediate: true,
+ deep: true,
+ handler (value) {
+ if (value.type === 'edit' && value.show) {
+ this.name = value.info.name
+ this.description = value.info.description
+ this.type = value.info.type || 'BASH'
+ this.groups = value.info.groups ? value.info.groups.map(x => x.id) : []
+ this.script = value.info.script
+ }
+ }
+ }
+ },
+ methods: {
+ setDialog (data) {
+ this.$store.commit('registration/setDialog', data)
+ },
+ async saveHook () {
+ await axios.post('/api/registrations/hooks/' + this.dialog.info.id, {
+ name: this.name,
+ description: this.description,
+ type: this.type,
+ groups: this.groups,
+ script: this.script
+ })
+ this.$store.dispatch('registration/loadHooks')
+ this.setDialog({ show: false })
+ }
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+.dialog-title {
+ z-index: 1;
+}
+</style>
diff --git a/webapp/src/config/dashboard.js b/webapp/src/config/dashboard.js
index 44c894f..a092cdc 100644
--- a/webapp/src/config/dashboard.js
+++ b/webapp/src/config/dashboard.js
@@ -1,11 +1,13 @@
import GroupModule from '@/components/GroupModule'
import ConfiguratorModule from '@/components/ConfiguratorModule'
+import RegistrationModule from '@/components/RegistrationModule'
import BackendModule from '@/components/BackendModule'
import PermissionModule from '@/components/PermissionModule'
export default [
{ path: 'groups', component: GroupModule, icon: 'category' },
{ path: 'configurator', component: ConfiguratorModule, icon: 'list' },
+ { path: 'registration', component: RegistrationModule, icon: 'assignment' },
{ path: 'backends', component: BackendModule, icon: 'cloud' },
{ path: 'permissions', component: PermissionModule, icon: 'lock_open' }
]
diff --git a/webapp/src/config/i18n.js b/webapp/src/config/i18n.js
index 91f8a79..818f57c 100644
--- a/webapp/src/config/i18n.js
+++ b/webapp/src/config/i18n.js
@@ -25,6 +25,7 @@ export default {
'$dashboardModules': {
'GroupModule': 'Groups / Clients',
'ConfiguratorModule': 'iPXE Configurator',
+ 'RegistrationModule': 'Client Registration',
'BackendModule': 'External Backends',
'PermissionModule': 'Permission Manager'
}
@@ -55,6 +56,7 @@ export default {
'$dashboardModules': {
'GroupModule': 'Gruppen / Clienten',
'ConfiguratorModule': 'iPXE Konfigurator',
+ 'RegistrationModule': 'Client Registrierung',
'BackendModule': 'Externe Backends',
'PermissionModule': 'Rechteverwaltung'
}
diff --git a/webapp/src/config/store.js b/webapp/src/config/store.js
index ca6b01f..710a07b 100644
--- a/webapp/src/config/store.js
+++ b/webapp/src/config/store.js
@@ -1,11 +1,13 @@
import groups from '@/store/groups'
import configurator from '@/store/configurator'
+import registration from '@/store/registration'
import backends from '@/store/backends'
import permissions from '@/store/permissions'
export default {
groups,
configurator,
+ registration,
backends,
permissions
}
diff --git a/webapp/src/store/registration.js b/webapp/src/store/registration.js
new file mode 100644
index 0000000..388558d
--- /dev/null
+++ b/webapp/src/store/registration.js
@@ -0,0 +1,40 @@
+import axios from 'axios'
+
+export default {
+ namespaced: true,
+ state: {
+ hooks: [],
+ groupList: [],
+ dialog: {
+ show: false,
+ type: null,
+ info: {}
+ }
+ },
+ mutations: {
+ setHooks (state, hooks) { state.hooks = hooks },
+ setGroupList (state, groupList) { state.groupList = groupList },
+ setDialog (state, { show, type, info }) {
+ if (info !== undefined) state.dialog.info = info
+ if (type !== undefined) state.dialog.type = type
+ if (show !== undefined) state.dialog.show = show
+ }
+ },
+ actions: {
+ loadHooks (context) {
+ axios.get('/api/registrations/hooks').then(result => {
+ context.commit('setHooks', result.data)
+ })
+ },
+ loadGroupList (context) {
+ axios.get('/api/groups/getList').then(result => {
+ context.commit('setGroupList', result.data)
+ })
+ },
+ setHooks (context, hooks) {
+ axios.post('/api/registrations/hookorder', { ids: hooks.map(x => x.id) }).then(result => {
+ context.commit('setHooks', hooks)
+ })
+ }
+ }
+}