summaryrefslogtreecommitdiffstats
path: root/webapp/src
diff options
context:
space:
mode:
authorChristian Hofmaier2019-03-24 17:02:18 +0100
committerChristian Hofmaier2019-03-24 17:02:18 +0100
commit1ee0e6c1d7484930387438b7ebb15340595b5383 (patch)
treecff87a96c777b9fee0c3e066d1bd6d88763f1915 /webapp/src
parent[webapp] small design fixes (diff)
downloadbas-1ee0e6c1d7484930387438b7ebb15340595b5383.tar.gz
bas-1ee0e6c1d7484930387438b7ebb15340595b5383.tar.xz
bas-1ee0e6c1d7484930387438b7ebb15340595b5383.zip
[eventmanager] Add module and functionality
- list to show all events, buttons to create/delete events - can add groups/clients to event - can add blacklist to event
Diffstat (limited to 'webapp/src')
-rw-r--r--webapp/src/components/EventModule.vue83
-rw-r--r--webapp/src/components/EventModuleDelete.vue66
-rw-r--r--webapp/src/components/EventModuleEdit.vue638
-rw-r--r--webapp/src/components/EventModuleEventList.vue85
-rw-r--r--webapp/src/components/PermissionModule.vue4
-rw-r--r--webapp/src/components/PermissionModuleEdit.vue1
-rw-r--r--webapp/src/components/PermissionModuleRoleList.vue2
-rw-r--r--webapp/src/config/dashboard.js4
-rw-r--r--webapp/src/config/i18n.js6
-rw-r--r--webapp/src/config/store.js4
-rw-r--r--webapp/src/store/events.js35
11 files changed, 921 insertions, 7 deletions
diff --git a/webapp/src/components/EventModule.vue b/webapp/src/components/EventModule.vue
new file mode 100644
index 0000000..68f9537
--- /dev/null
+++ b/webapp/src/components/EventModule.vue
@@ -0,0 +1,83 @@
+<i18n>
+{
+ "en": {
+ "events": "Events",
+ "eventCalendar": "Event Calendar",
+ "eventList": "Event List"
+ },
+ "de": {
+ "events": "Veranstaltungen",
+ "eventCalendar": "Veranstaltungskalendar",
+ "eventList": "Veranstaltungsliste"
+ }
+}
+</i18n>
+
+<template>
+ <v-container fill-height>
+ <v-layout>
+ <v-flex xl10 offset-xl1 lg12>
+ <v-card class="tabbar-card">
+ <v-tabs v-model="tabs" grow hide-slider :dark="tabsDark" :color="tabsColor" :slider-color="tabsSliderColor">
+ <v-tab><v-icon class="tabbar-tabicon">calendar_view_day</v-icon>{{ $t('eventList') }}</v-tab>
+ </v-tabs>
+ </v-card>
+ <v-tabs-items v-model="tabs" style="padding-bottom: 20px">
+ <v-tab-item>
+ <v-subheader>{{ $t('eventList') }}</v-subheader>
+ <event-module-event-list/>
+ </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' : '1200px'"
+ scrollable
+ persistent
+ :fullscreen="$vuetify.breakpoint.smAndDown"
+ >
+ <event-module-delete v-if="dialog.type === 'delete'" />
+ <event-module-edit v-else-if="dialog.type === 'edit'"/>
+ </v-dialog>
+ </v-container>
+</template>
+
+<script>
+import EventModuleEventList from '@/components/EventModuleEventList'
+import EventModuleEdit from '@/components/EventModuleEdit'
+import EventModuleDelete from '@/components/EventModuleDelete'
+import { mapState, mapGetters, mapMutations } from 'vuex'
+
+export default {
+ name: 'EventModule',
+ components: {
+ EventModuleEventList,
+ EventModuleEdit,
+ EventModuleDelete
+ },
+ data () {
+ return {
+ tabs: 0
+ }
+ },
+ computed: {
+ ...mapGetters(['tabsDark', 'tabsColor', 'tabsSliderColor']),
+ ...mapState('events', ['dialog'])
+ },
+ watch: {
+ },
+ methods: {
+ ...mapMutations('events', ['setDialog'])
+ },
+ created () {
+ this.$store.dispatch('events/loadLists')
+ this.$store.dispatch('groups/loadLists')
+ }
+}
+</script>
+
+<style scoped>
+
+</style>
diff --git a/webapp/src/components/EventModuleDelete.vue b/webapp/src/components/EventModuleDelete.vue
new file mode 100644
index 0000000..f22fc41
--- /dev/null
+++ b/webapp/src/components/EventModuleDelete.vue
@@ -0,0 +1,66 @@
+<i18n>
+{
+ "en": {
+ "delete-are-you-sure": "Are you sure you want to delete this event? | Are you sure you want to delete these events?"
+ },
+ "de": {
+ "delete-are-you-sure": "Sind sie sicher, dass sie diese Veranstaltung Löschen wollen? | Sind sie sicher, dass sie diese Veranstaltungen löschen wollen?"
+ }
+}
+</i18n>
+
+<template>
+ <v-card>
+ <v-card-title class="elevation-3">
+ <div class="headline">{{ $t('title') }}</div>
+ </v-card-title>
+ <v-card-text>
+ {{ $tc('delete-are-you-sure', dialog.info.length) }}
+ <template v-for="item in dialog.info">
+ <div class="grey--text" :key="item.id">{{ item.name }} ({{ item.description }})</div>
+ </template>
+ </v-card-text>
+ <v-divider></v-divider>
+ <v-card-actions>
+ <v-spacer></v-spacer>
+ <v-btn flat="flat" @click="cancelDelete">{{ $t('cancel') }}</v-btn>
+ <v-btn color="error" @click="deleteEvents">{{ $t('delete') }}</v-btn>
+ </v-card-actions>
+ </v-card>
+</template>
+
+<script>
+import { mapState, mapMutations } from 'vuex'
+
+export default {
+ name: 'EventModuleDelete',
+ data () {
+ return {
+ }
+ },
+ computed: {
+ ...mapState('events', ['dialog'])
+ },
+ methods: {
+ ...mapMutations('events', ['setDialog', 'loadEvents']),
+ cancelDelete () {
+ this.setDialog({ show: false })
+ },
+ async deleteEvents () {
+ var deleteIds = []
+ for (let i = 0; i < this.dialog.info.length; i++) {
+ deleteIds.push(this.dialog.info[i].id)
+ }
+ await this.$http.post('/api/events?delete', { ids: deleteIds })
+ this.setDialog({ show: false })
+ this.$snackbar({ color: 'success', text: this.$t('eventDeleteSuccess') })
+ this.$store.dispatch('events/loadEvents')
+ }
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+</style>
diff --git a/webapp/src/components/EventModuleEdit.vue b/webapp/src/components/EventModuleEdit.vue
new file mode 100644
index 0000000..2b6dbc3
--- /dev/null
+++ b/webapp/src/components/EventModuleEdit.vue
@@ -0,0 +1,638 @@
+<i18n>
+{
+ "en": {
+ "april": "April",
+ "august": "August",
+ "blacklist": "Blacklist",
+ "blacklistClients": "Blacklist Clients",
+ "blacklistGroups": "Blacklist Groups",
+ "clients": "Clients",
+ "config": "Configuration",
+ "day": "Day",
+ "days": "Days",
+ "december": "December",
+ "description": "Description",
+ "endDate": "End Date",
+ "endTime": "End Time",
+ "event": "Event",
+ "eventName": "Event Name",
+ "eventNameEmptyError": "Event Name can't be empty!",
+ "february": "February",
+ "friday": "Friday",
+ "groups": "Groups",
+ "groupsAndClients": "Groups / Clients",
+ "id": "ID",
+ "importantEvent": "Important Event",
+ "interval": "Interval",
+ "intervalMustBeNumberError": "Interval must be a valid number!",
+ "intervalTypes": "Interval Types",
+ "ip": "IP",
+ "january": "January",
+ "july": "July",
+ "june": "June",
+ "march": "March",
+ "may": "May",
+ "monday": "Monday",
+ "month": "Month",
+ "months": "Months",
+ "name": "Name",
+ "november": "November",
+ "october": "October",
+ "repetitiveModeSwitch": "repetitive Event",
+ "saturday": "Saturday",
+ "september": "September",
+ "startDate": "Start Date",
+ "startTime": "Start Time",
+ "sunday": "Sunday",
+ "thursday": "Thursday",
+ "tuesday": "Tuesday",
+ "uuid": "UUID",
+ "wednesday": "Wednesday",
+ "week": "Week"
+ },
+ "de": {
+ "april": "April",
+ "august": "August",
+ "blacklist": "Blacklist",
+ "blacklistClients": "Blacklist Clienten",
+ "blacklistGroups": "Blacklist Gruppen",
+ "clients": "Clients",
+ "config": "Konfiguration",
+ "day": "Tag",
+ "days": "Tage",
+ "december": "Dezember",
+ "description": "Beschreibung",
+ "endDate": "End Datum",
+ "endTime": "End Zeit",
+ "event": "Veranstaltung",
+ "eventName": "Veranstaltungsname",
+ "eventNameEmptyError": "Der Veranstaltungsname kann nicht leer sein!",
+ "february": "Februar",
+ "friday": "Freitag",
+ "groups": "Gruppen",
+ "groupsAndClients": "Gruppen / Clients",
+ "id": "ID",
+ "importantEvent": "Wichtige Veranstaltung",
+ "interval": "Intervall",
+ "intervalMustBeNumberError": "Das Intervall muss eine gültige Zahl sein!",
+ "intervalTypes": "Intervalltypen",
+ "ip": "IP",
+ "january": "Januar",
+ "july": "Juli",
+ "june": "Juni",
+ "march": "März",
+ "may": "Mai",
+ "monday": "Montag",
+ "month": "Monat",
+ "months": "Monate",
+ "name": "Name",
+ "november": "November",
+ "october": "Oktober",
+ "repetitiveModeSwitch": "Wiederholende Veranstaltung",
+ "saturday": "Samstag",
+ "september": "September",
+ "startDate": "Start Datum",
+ "startTime": "Start Zeit",
+ "sunday": "Sonntag",
+ "thursday": "Donnerstag",
+ "tuesday": "Dienstag",
+ "uuid": "UUID",
+ "wednesday": "Mittwoch",
+ "week": "Woche"
+ }
+}
+</i18n>
+
+<template>
+ <v-card>
+ <v-card-title style="padding: 0px">
+ <v-stepper v-model="step" horizontal style="width: 100%; background: transparent;" class="elevation-3">
+ <v-stepper-header>
+ <v-stepper-step
+ :complete="stepCompleted >= 1"
+ step="1"
+ :editable="stepCompleted >= 0"
+ edit-icon="check"
+ >
+ {{ $t('event') }}
+ </v-stepper-step>
+ <v-divider></v-divider>
+ <v-stepper-step
+ :complete="stepCompleted >= 2"
+ step="2"
+ :editable="stepCompleted >= 1"
+ edit-icon="check"
+ >
+ {{ $t('groupsAndClients') }}
+ </v-stepper-step>
+ <v-divider></v-divider>
+ <v-stepper-step
+ :complete="stepCompleted >= 3"
+ step="3"
+ :editable="stepCompleted >= 2"
+ edit-icon="check"
+ >
+ {{ $t('blacklist') }}
+ </v-stepper-step>
+ </v-stepper-header>
+ </v-stepper>
+ </v-card-title>
+ <v-card-text class="table-container">
+ <v-form v-model="valid" ref="form" @submit.prevent="submit" lazy-validation>
+ <v-stepper v-model="step" horizontal style="width: 100%; background: transparent" class="elevation-0">
+ <v-stepper-items>
+ <v-stepper-content step="1">
+ <v-layout row wrap>
+ <v-flex xs12 md5>
+ <v-text-field
+ v-model="name"
+ :label="$t('eventName')"
+ prepend-icon="label"
+ color="primary"
+ :rules="[() => !!name || $t('eventNameEmptyError')]"
+ ref="name"
+ ></v-text-field>
+ </v-flex>
+ <v-flex xs12 md5 offset-md1>
+ <select-box
+ prepend-icon="device_hub"
+ :label="$t('config')"
+ v-model="config"
+ :items="configList"
+ single-select
+ :max-columns="1"
+ ></select-box>
+ </v-flex>
+ <v-flex xs12 md5>
+ <v-textarea
+ v-model="description"
+ :label="$t('description')"
+ prepend-icon="description"
+ color="primary"
+ rows="1"
+ auto-grow
+ ></v-textarea>
+ </v-flex>
+ <v-flex xs12 md5 offset-md1>
+ <v-checkbox
+ v-model="important"
+ color="primary"
+ :label="$t('importantEvent')"
+ ></v-checkbox>
+ </v-flex>
+ </v-layout>
+ <v-layout row wrap>
+ <v-flex xs12 md5>
+ <v-menu
+ v-model="startDateMenu"
+ transition="scale-transition"
+ :close-on-content-click="false"
+ lazy
+ offset-y
+ full-width
+ min-width="290px"
+ >
+ <template v-slot:activator="{ on }">
+ <v-text-field
+ v-model="startDate"
+ :label="$t('startDate')"
+ prepend-icon="event"
+ readonly
+ v-on="on"
+ ></v-text-field>
+ </template>
+ <v-date-picker color="primary" header-color="primary" v-model="startDate" @input="startDatePick()"></v-date-picker>
+ </v-menu>
+ </v-flex>
+ <v-flex xs12 md5 offset-md1>
+ <v-menu
+ v-model="endDateMenu"
+ transition="scale-transition"
+ :close-on-content-click="false"
+ lazy
+ offset-y
+ full-width
+ min-width="290px"
+ >
+ <template v-slot:activator="{ on }">
+ <v-text-field
+ v-model="endDate"
+ :label="$t('endDate')"
+ prepend-icon="event"
+ readonly
+ v-on="on"
+ ></v-text-field>
+ </template>
+ <v-date-picker color="primary" header-color="primary" :min="startDate" v-model="endDate" @input="endDatePick()"></v-date-picker>
+ </v-menu>
+ </v-flex>
+ <v-flex xs12 md5>
+ <v-menu
+ v-model="startTimeMenu"
+ transition="scale-transition"
+ :close-on-content-click="false"
+ lazy
+ offset-y
+ full-width
+ max-width="290px"
+ min-width="290px"
+ >
+ <template v-slot:activator="{ on }">
+ <v-text-field
+ v-model="startTime"
+ :label="$t('startTime')"
+ prepend-icon="access_time"
+ readonly
+ v-on="on"
+ ></v-text-field>
+ </template>
+ <v-time-picker
+ v-model="startTime"
+ full-width
+ format="24hr"
+ color="primary"
+ header-color="primary"
+ @click:minute="startTimeMenu = false"
+ ></v-time-picker>
+ </v-menu>
+ </v-flex>
+ <v-flex xs12 md5 offset-md1>
+ <v-menu
+ v-model="endTimeMenu"
+ transition="scale-transition"
+ :close-on-content-click="false"
+ lazy
+ offset-y
+ full-width
+ max-width="290px"
+ min-width="290px"
+ >
+ <template v-slot:activator="{ on }">
+ <v-text-field
+ v-model="endTime"
+ :label="$t('endTime')"
+ prepend-icon="access_time"
+ readonly
+ v-on="on"
+ ></v-text-field>
+ </template>
+ <v-time-picker
+ v-model="endTime"
+ :min="startDate === endDate ? startTime : '00:00'"
+ full-width
+ format="24hr"
+ color="primary"
+ header-color="primary"
+ @click:minute="endTimeMenu = false"
+ ></v-time-picker>
+ </v-menu>
+ </v-flex>
+ </v-layout>
+ <v-layout row wrap>
+ <v-flex>
+ <v-switch color="primary" v-model="repetitiveEvent" :label="$t('repetitiveModeSwitch')"/>
+ </v-flex>
+ </v-layout>
+ <v-layout row wrap v-show="repetitiveEvent">
+ <v-flex xs12 md12>
+ <p style="margin-bottom: 0px">{{ $t('days') }}</p>
+ <v-btn small class="no-margin" :color="dayMap[0] ? 'primary' : ''" @click="setDayMap(0)">{{ $t('monday') }}</v-btn>
+ <v-btn small class="no-margin" :color="dayMap[1] ? 'primary' : ''" @click="setDayMap(1)">{{ $t('tuesday') }}</v-btn>
+ <v-btn small class="no-margin" :color="dayMap[2] ? 'primary' : ''" @click="setDayMap(2)">{{ $t('wednesday') }}</v-btn>
+ <v-btn small class="no-margin" :color="dayMap[3] ? 'primary' : ''" @click="setDayMap(3)">{{ $t('thursday') }}</v-btn>
+ <v-btn small class="no-margin" :color="dayMap[4] ? 'primary' : ''" @click="setDayMap(4)">{{ $t('friday') }}</v-btn>
+ <v-btn small class="no-margin" :color="dayMap[5] ? 'primary' : ''" @click="setDayMap(5)">{{ $t('saturday') }}</v-btn>
+ <v-btn small class="no-margin" :color="dayMap[6] ? 'primary' : ''" @click="setDayMap(6)">{{ $t('sunday') }}</v-btn>
+ </v-flex>
+ <v-flex xs12 md12 style="margin-top: 20px; margin-bottom: 20px">
+ <p style="margin-bottom: 0px">{{ $t('months') }}</p>
+ <v-btn small class="no-margin" :color="monthMap[0] ? 'primary' : ''" @click="setMonthMap(0)">{{ $t('january') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[1] ? 'primary' : ''" @click="setMonthMap(1)">{{ $t('february') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[2] ? 'primary' : ''" @click="setMonthMap(2)">{{ $t('march') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[3] ? 'primary' : ''" @click="setMonthMap(3)">{{ $t('april') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[4] ? 'primary' : ''" @click="setMonthMap(4)">{{ $t('may') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[5] ? 'primary' : ''" @click="setMonthMap(5)">{{ $t('june') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[6] ? 'primary' : ''" @click="setMonthMap(6)">{{ $t('july') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[7] ? 'primary' : ''" @click="setMonthMap(7)">{{ $t('august') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[8] ? 'primary' : ''" @click="setMonthMap(8)">{{ $t('september') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[9] ? 'primary' : ''" @click="setMonthMap(9)">{{ $t('october') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[10] ? 'primary' : ''" @click="setMonthMap(10)">{{ $t('november') }}</v-btn>
+ <v-btn small class="no-margin" :color="monthMap[11] ? 'primary' : ''" @click="setMonthMap(11)">{{ $t('december') }}</v-btn>
+ </v-flex>
+ <v-flex xs12 md5>
+ <v-text-field
+ v-model="interval"
+ :label="$t('interval')"
+ prepend-icon="repeat"
+ color="primary"
+ type="number"
+ min="1"
+ :rules="[() => !!interval || $t('intervalMustBeNumberError')]"
+ ></v-text-field>
+ </v-flex>
+ <v-flex xs12 md5 offset-md1>
+ <v-select :items="intervalTypes" :label="$t('intervalTypes')" v-model="intervalType" prepend-icon="repeat" color="primary" offset-y></v-select>
+ </v-flex>
+ </v-layout>
+ </v-stepper-content>
+
+ <v-stepper-content step="2">
+ <v-tabs v-model="tabs" grow slider-color="primary">
+ <v-tab><v-icon class="tabbar-tabicon">category</v-icon>{{ groups.length + ' ' + $t('groups') }}</v-tab>
+ <v-tab><v-icon class="tabbar-tabicon">computer</v-icon>{{ clients.length + ' ' + $t('clients') }}</v-tab>
+ </v-tabs>
+ <v-tabs-items v-model="tabs">
+ <v-tab-item>
+ <data-table v-model="groups" :headers="groupHeaders" :items="groupList"/>
+ </v-tab-item>
+ <v-tab-item>
+ <data-table v-model="clients" :headers="clientHeaders" :items="clientList"/>
+ </v-tab-item>
+ </v-tabs-items>
+ </v-stepper-content>
+
+ <v-stepper-content step="3">
+ <v-tabs v-model="tabs" grow slider-color="primary">
+ <v-tab><v-icon class="tabbar-tabicon">category</v-icon>{{ blacklistGroups.length + ' ' + $t('blacklistGroups') }}</v-tab>
+ <v-tab><v-icon class="tabbar-tabicon">computer</v-icon>{{ blacklistClients.length + ' ' + $t('blacklistClients') }}</v-tab>
+ </v-tabs>
+ <v-tabs-items v-model="tabs">
+ <v-tab-item>
+ <data-table v-model="blacklistGroups" :headers="groupHeaders" :items="blackgroupList"/>
+ </v-tab-item>
+ <v-tab-item>
+ <data-table v-model="blacklistClients" :headers="clientHeaders" :items="blackclientList"/>
+ </v-tab-item>
+ </v-tabs-items>
+ </v-stepper-content>
+
+ </v-stepper-items>
+ </v-stepper>
+ </v-form>
+ </v-card-text>
+ <v-divider></v-divider>
+ <v-card-actions>
+ <v-spacer></v-spacer>
+ <v-btn flat @click.native="$store.commit('events/setDialog', { show : false } )">{{ $t('cancel') }}</v-btn>
+ <v-btn color="primary" v-show="step == 1" @click.native="completeStepOne()">{{ $t('continue') }}</v-btn>
+ <v-btn :color="dialog.info.id ? 'primary' : 'success'" v-show="step == 2" @click="submit" type="submit">{{ dialog.info.id ? $t('save') : $t('create') }}</v-btn>
+ <v-btn color="primary" v-show="step == 2" @click.native="completeStepTwo()">{{ $t('continue') }}</v-btn>
+ <v-btn :color="dialog.info.id ? 'primary' : 'success'" v-show="step == 3" @click="submit" type="submit">{{ dialog.info.id ? $t('save') : $t('create') }}</v-btn>
+ </v-card-actions>
+ </v-card>
+</template>
+
+<script>
+import SelectBox from '@/components/SelectBox'
+import DataTable from '@/components/DataTable'
+import { mapState } from 'vuex'
+
+export default {
+ name: 'EventModuleEdit',
+ components: {
+ SelectBox,
+ DataTable
+ },
+ data () {
+ return {
+ tabs: 0,
+ startDate: new Date().toISOString().substr(0, 10),
+ endDate: new Date().toISOString().substr(0, 10),
+ startTime: '00:00',
+ endTime: '23:59',
+ startDateMenu: false,
+ endDateMenu: false,
+ startTimeMenu: false,
+ endTimeMenu: false,
+ repetitiveEvent: false,
+ dayMap: [],
+ monthMap: [],
+ interval: 1,
+ intervalType: 'day',
+ valid: true,
+ step: 1,
+ stepCompleted: 0,
+ name: '',
+ description: '',
+ important: false,
+ config: [],
+ times: {},
+ groups: [],
+ blacklistGroups: [],
+ blackgroupList: [],
+ clients: [],
+ blacklistClients: [],
+ blackclientList: [],
+ clientHeaders: [
+ { text: this.$t('name'), key: 'name' },
+ { text: this.$t('ip'), key: 'ip' },
+ { text: this.$t('uuid'), key: 'uuid' }
+ ],
+ groupHeaders: [
+ { text: this.$t('id'), key: 'id' },
+ { text: this.$t('name'), key: 'name' },
+ { text: this.$t('description'), key: 'description' }
+ ],
+ intervalTypes: []
+ }
+ },
+ computed: {
+ ...mapState('events', ['dialog', 'configList']),
+ ...mapState('groups', ['groupList', 'clientList'])
+ },
+ watch: {
+ dialog: {
+ immediate: true,
+ deep: true,
+ handler (value) {
+ if (value.type === 'edit' && value.show) {
+ if (this.$refs.form) this.$refs.form.resetValidation()
+ this.step = 1
+ this.intervalTypes = []
+ this.intervalTypes.push(this.$t('day'))
+ this.intervalTypes.push(this.$t('week'))
+ this.intervalTypes.push(this.$t('month'))
+ this.stepCompleted = value.info.id ? 4 : 0
+ this.name = value.info.name || ''
+ this.description = value.info.description || ''
+ this.important = value.info.important || false
+
+ this.config = []
+ if (value.info.config) {
+ for (let i = 0; i < this.configList.length; i++) {
+ if (this.configList[i].id === value.info.config) {
+ this.config.push(this.configList[i])
+ }
+ }
+ }
+
+ this.times = value.info.times ? JSON.parse(value.info.times) : {}
+ this.repetitiveEvent = this.times.repetitive || false
+ if (this.repetitiveEvent) {
+ // edit of repetitive event
+ this.startDate = new Date(this.times.startDate * 1000)
+ this.startDate = this.formatDate(this.startDate, { time: false })
+ this.endDate = new Date(this.times.endDate * 1000)
+ this.endDate = this.formatDate(this.endDate, { time: false })
+ this.startTime = this.times.startTime
+ this.endTime = this.times.endTime
+ } else if (value.info.times) {
+ // edit of non repetitive event
+ var start = new Date(this.times.start * 1000)
+ var end = new Date(this.times.end * 1000)
+ this.startDate = this.formatDate(start, { time: false })
+ this.endDate = this.formatDate(end, { time: false })
+ this.startTime = this.formatDate(start, { date: false, seconds: false })
+ this.endTime = this.formatDate(end, { date: false, seconds: false })
+ } else {
+ // create new event
+ this.startDate = this.formatDate(new Date(), { time: false })
+ this.endDate = this.formatDate(new Date(), { time: false })
+ this.startTime = '00:00'
+ this.endTime = '23:59'
+ }
+ this.interval = this.times.interval || 1
+ this.intervalType = this.times.intervalType || 'day'
+ this.dayMap = this.times.dayMap || [1, 1, 1, 1, 1, 1, 1]
+ this.monthMap = this.times.monthMap || [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+
+ if (value.info.groups) {
+ this.groups = []
+ this.clients = []
+ var groupsPush = []
+ var clientsPush = []
+ for (let i = 0; i < value.info.groups.length; i++) {
+ if (value.info.groups[i].group_x_event.blacklist) groupsPush.push(value.info.groups[i])
+ else this.groups.push(value.info.groups[i])
+ }
+ for (let i = 0; i < value.info.clients.length; i++) {
+ if (value.info.clients[i].client_x_event.blacklist) clientsPush.push(value.info.clients[i])
+ else this.clients.push(value.info.clients[i])
+ }
+ this.loadChilds().then(() => {
+ this.blacklistGroups = groupsPush
+ this.blacklistClients = clientsPush
+ })
+ } else {
+ this.groups = []
+ this.clients = value.info.clients ? value.info.clients : []
+ }
+ }
+ }
+ }
+ },
+ methods: {
+ formatDate (date, options = {}) {
+ var result = ''
+ const pad = x => x < 10 ? '0' + x : x
+ if (options.date !== false) {
+ result += date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate())
+ }
+ if (options.time !== false) {
+ if (result !== '') result += ' '
+ result += pad(date.getHours()) + ':' + pad(date.getMinutes())
+ if (options.seconds !== false) result += ':' + pad(date.getSeconds())
+ }
+ return result
+ },
+ completeStepOne () {
+ if (this.$refs.form.validate()) {
+ this.step = 2
+ this.stepCompleted = Math.max(1, this.stepCompleted)
+ }
+ },
+ completeStepTwo () {
+ if (this.groups.length > 0) {
+ this.loadChilds()
+ }
+ this.step = 3
+ this.stepCompleted = Math.max(2, this.stepCompleted)
+ },
+ async submit (event) {
+ if (this.$refs.form.validate()) {
+ // Build times object
+ if (this.repetitiveEvent) {
+ var startD = new Date(this.startDate)
+ var endD = new Date(this.endDate)
+ this.times = {
+ 'repetitive': true,
+ 'startDate': (startD.getTime() / 1000),
+ 'endDate': (endD.getTime() / 1000),
+ 'startTime': this.startTime,
+ 'endTime': this.endTime,
+ 'dayMap': this.dayMap,
+ 'monthMap': this.monthMap,
+ 'interval': this.interval,
+ 'intervalType': this.intervalType
+ }
+ } else {
+ var start = new Date(this.startDate)
+ var startSplit = this.startTime.split(':')
+ start.setHours(startSplit[0])
+ start.setMinutes(startSplit[1])
+ var end = new Date(this.endDate)
+ var endSplit = this.endTime.split(':')
+ end.setHours(endSplit[0])
+ end.setMinutes(endSplit[1])
+ this.times = {
+ 'repetitive': false,
+ 'start': (start.getTime() / 1000),
+ 'end': (end.getTime() / 1000)
+ }
+ }
+ // Submit
+ await this.$http.post('/api/events/' + this.dialog.info.id, {
+ name: this.name,
+ description: this.description,
+ groups: this.groups.map(x => x.id),
+ blacklistGroups: this.blacklistGroups.map(x => x.id),
+ clients: this.clients.map(x => x.id),
+ blacklistClients: this.blacklistClients.map(x => x.id),
+ config: this.config.map(x => x.id),
+ important: this.important,
+ times: JSON.stringify(this.times)
+ })
+ this.$store.dispatch('events/loadEvents')
+ this.$store.commit('events/setDialog', { show: false })
+ this.$snackbar({ color: 'success', text: this.$t('eventSavedSuccess') })
+ } else {
+ this.$snackbar({ color: 'error', text: this.$t('eventNameEmptyError') })
+ }
+ },
+ startDatePick () {
+ this.startDateMenu = false
+ if (this.startDate > this.endDate) {
+ this.endDate = this.startDate
+ }
+ },
+ endDatePick () {
+ this.endDateMenu = false
+ if (this.endTime < this.startTime) {
+ this.endTime = this.startTime
+ }
+ },
+ setDayMap (i) {
+ this.$set(this.dayMap, i, this.dayMap[i] ? 0 : 1)
+ },
+ setMonthMap (i) {
+ this.$set(this.monthMap, i, this.monthMap[i] ? 0 : 1)
+ },
+ async loadChilds () {
+ var response = await this.$http.post('/api/events/blacklist', { groups: this.groups })
+ this.blackgroupList = response.data.subgroups
+ this.blackclientList = response.data.clients
+ }
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+.info-input {
+ margin: 20px;
+}
+.no-margin {
+ margin-right: 0px;
+ margin-left: 0px;
+}
+</style>
diff --git a/webapp/src/components/EventModuleEventList.vue b/webapp/src/components/EventModuleEventList.vue
new file mode 100644
index 0000000..fe2ac1a
--- /dev/null
+++ b/webapp/src/components/EventModuleEventList.vue
@@ -0,0 +1,85 @@
+<i18n>
+{
+ "en": {
+ "create-event": "Create Event",
+ "delete-event": "Delete {0} Event | Delete {0} Events",
+ "name": "Name",
+ "description": "Description",
+ "config": "Configuration",
+ "times": "Times"
+ },
+ "de": {
+ "create-event": "Veranstaltung erstellen",
+ "delete-event": "Lösche {0} Veranstaltung | Lösche {0} Veranstaltungen",
+ "name": "Name",
+ "description": "Beschreibung",
+ "config": "Konfiguration",
+ "times": "Zeiten"
+ }
+}
+</i18n>
+
+<template>
+ <div>
+ <v-card>
+ <data-table v-model="selectedEvents" :headers="headers" :items="events">
+ <div slot="action" slot-scope="row" style="text-align: right">
+ <v-btn flat icon color="primary" @click.stop="editEvent(row.item)"><v-icon>edit</v-icon></v-btn>
+ </div>
+ </data-table>
+ </v-card>
+ <div class="text-xs-right">
+ <v-btn color="error" flat @click="deleteEvent" :disabled="selectedEvents.length === 0">
+ <v-icon left>remove_circle_outline</v-icon>{{ $tc('delete-event', selectedEvents.length, [selectedEvents.length]) }}
+ </v-btn>
+ <v-btn color="success" flat @click="createEvent">
+ <v-icon left>add_circle_outline</v-icon>{{ $t('create-event') }}
+ </v-btn>
+ </div>
+ </div>
+</template>
+
+<script>
+import { mapState, mapMutations } from 'vuex'
+import DataTable from '@/components/DataTable'
+
+export default {
+ name: 'EventModuleEventList',
+ components: {
+ DataTable
+ },
+ data () {
+ return {
+ selectedEvents: []
+ }
+ },
+ computed: {
+ headers () {
+ return [
+ { text: this.$t('name'), key: 'name' },
+ { text: this.$t('description'), key: 'description' },
+ { text: this.$t('config'), key: 'config' },
+ { sortable: false, key: 'action', width: '60px' }
+ ]
+ },
+ ...mapState('events', ['events'])
+ },
+ methods: {
+ ...mapMutations('events', ['setDialog']),
+ createEvent () {
+ this.setDialog({ show: true, type: 'edit', info: {} })
+ },
+ deleteEvent () {
+ this.setDialog({ show: true, type: 'delete', info: this.selectedEvents })
+ },
+ editEvent (event) {
+ this.setDialog({ show: true, type: 'edit', info: event })
+ }
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+</style>
diff --git a/webapp/src/components/PermissionModule.vue b/webapp/src/components/PermissionModule.vue
index 213ca4f..109007c 100644
--- a/webapp/src/components/PermissionModule.vue
+++ b/webapp/src/components/PermissionModule.vue
@@ -3,14 +3,14 @@
"en": {
"roles": "Roles",
"users": "Users",
- "delete-are-you-sure": "Are you sure you want to delete this role? | Are you sure you want to delete those roles?",
+ "delete-are-you-sure": "Are you sure you want to delete this role? | Are you sure you want to delete these roles?",
"delete-permission": "Delete {0} role | Delete {0} roles",
"delete-role": "Delete {0} Role | Delete {0} Roles"
},
"de": {
"roles": "Rollen",
"users": "Nutzer",
- "delete-are-you-sure": "Sind sie sicher, dass sie diese Rolle Löschen wollen? | Sind sie sicher, dass sie diese Rollen Löschen wollen?",
+ "delete-are-you-sure": "Sind sie sicher, dass sie diese Rolle Löschen wollen? | Sind sie sicher, dass sie diese Rollen löschen wollen?",
"delete-permission": "Delete {0} role | Delete {0} roles",
"delete-role": "Lösche {0} Rolle | Lösche {0} Rollen"
}
diff --git a/webapp/src/components/PermissionModuleEdit.vue b/webapp/src/components/PermissionModuleEdit.vue
index c026f54..f88eb4c 100644
--- a/webapp/src/components/PermissionModuleEdit.vue
+++ b/webapp/src/components/PermissionModuleEdit.vue
@@ -274,6 +274,7 @@ export default {
}).then(response => {
this.$snackbar({ color: 'success', text: this.$t('roleSavedSuccess') })
this.$store.dispatch('permissions/loadRoleData')
+ this.$store.dispatch('permissions/loadUserData')
this.$store.commit('permissions/setEdit', false)
}).catch(error => {
console.log(error)
diff --git a/webapp/src/components/PermissionModuleRoleList.vue b/webapp/src/components/PermissionModuleRoleList.vue
index 7b744b6..67fbb9f 100644
--- a/webapp/src/components/PermissionModuleRoleList.vue
+++ b/webapp/src/components/PermissionModuleRoleList.vue
@@ -52,7 +52,7 @@ export default {
{ text: this.$t('id'), key: 'id' },
{ text: this.$t('name'), key: 'name' },
{ text: this.$t('description'), key: 'descr' },
- { sortable: false, key: 'action' }
+ { sortable: false, key: 'action', width: '60px' }
],
canEdit: false
}
diff --git a/webapp/src/config/dashboard.js b/webapp/src/config/dashboard.js
index 26de0fc..7a5244e 100644
--- a/webapp/src/config/dashboard.js
+++ b/webapp/src/config/dashboard.js
@@ -6,6 +6,7 @@ import PermissionModule from '@/components/PermissionModule'
import IpxeBuilderModule from '@/components/IpxeBuilderModule'
import UserModule from '@/components/UserModule'
import LogModule from '@/components/LogModule'
+import EventModule from '@/components/EventModule'
export default [
{ path: 'groups', component: GroupModule, icon: 'category' },
@@ -15,5 +16,6 @@ export default [
{ path: 'permissions', component: PermissionModule, icon: 'lock_open' },
{ path: 'ipxe', component: IpxeBuilderModule, icon: 'merge_type' },
{ path: 'users', component: UserModule, icon: 'contacts' },
- { path: 'log', component: LogModule, icon: 'error_outline' }
+ { path: 'log', component: LogModule, icon: 'error_outline' },
+ { path: 'events', component: EventModule, icon: 'event' }
]
diff --git a/webapp/src/config/i18n.js b/webapp/src/config/i18n.js
index c9cf137..25e6f3a 100644
--- a/webapp/src/config/i18n.js
+++ b/webapp/src/config/i18n.js
@@ -30,7 +30,8 @@ export default {
'PermissionModule': 'Permission Manager',
'IpxeBuilderModule': 'iPXE Builder',
'UserModule': 'User Management',
- 'LogModule': 'System Log'
+ 'LogModule': 'System Log',
+ 'EventModule': 'Event Manager'
}
},
'de': {
@@ -64,7 +65,8 @@ export default {
'PermissionModule': 'Rechteverwaltung',
'IpxeBuilderModule': 'iPXE Builder',
'UserModule': 'Benutzerverwaltung',
- 'LogModule': 'System Protokoll'
+ 'LogModule': 'System Protokoll',
+ 'EventModule': 'Veranstaltungsverwaltung'
}
}
}
diff --git a/webapp/src/config/store.js b/webapp/src/config/store.js
index ac65696..f8bb4f0 100644
--- a/webapp/src/config/store.js
+++ b/webapp/src/config/store.js
@@ -5,6 +5,7 @@ import registration from '@/store/registration'
import backends from '@/store/backends'
import permissions from '@/store/permissions'
import users from '@/store/users'
+import events from '@/store/events'
export default {
notifications,
@@ -13,5 +14,6 @@ export default {
registration,
backends,
permissions,
- users
+ users,
+ events
}
diff --git a/webapp/src/store/events.js b/webapp/src/store/events.js
new file mode 100644
index 0000000..27cb3e7
--- /dev/null
+++ b/webapp/src/store/events.js
@@ -0,0 +1,35 @@
+import axios from 'axios'
+
+export default {
+ namespaced: true,
+ state: {
+ events: [],
+ configList: [],
+ dialog: { show: false, type: null, info: {} }
+ },
+ mutations: {
+ setEvents (state, value) { state.events = value },
+ setConfigList (state, value) { state.configList = value },
+ setDialog (state, { show, type, info }) {
+ if (show !== undefined) state.dialog.show = show
+ if (type !== undefined) state.dialog.type = type
+ if (info !== undefined) state.dialog.info = info
+ }
+ },
+ actions: {
+ loadEvents (context) {
+ axios.get('/api/events').then(response => {
+ context.commit('setEvents', response.data)
+ })
+ },
+ loadConfigs (context) {
+ axios.get('/api/configurator/configs').then(result => {
+ context.commit('setConfigList', result.data)
+ })
+ },
+ loadLists (context) {
+ context.dispatch('loadEvents')
+ context.dispatch('loadConfigs')
+ }
+ }
+}