summaryrefslogblamecommitdiffstats
path: root/webapp/src/components/IprangeModule.vue
blob: 5d1ff4650b220fbe644a5b2f5615b9f96934d139 (plain) (tree)











































                                                                                   
                                                                                                                                 








                                                                                                                                                                                          
                                                                                                                                                     





                                                                   
                                                                                                                                                                             



                           

                                                                                                                                        

                                                                                                           


                                                                                                                     











                                                               




























                                                                                                                                                                                        
                                                                                    































                                                                                          


                                                                                       
                                                                                             





















































































                                                                                                                     
<i18n>
{
  "en": {
    "ipranges": "IP Ranges",
    "deleteIpranges": "Delete one iprange | Delete {0} ipranges",
    "createIprange": "Create IP range",
    "id": "ID",
    "startIp": "Start IP",
    "endIp": "End IP",
    "group": "Group",
    "deleteTitle": "Delete this IP range? | Delete these {0} IP ranges?",
    "createTitle": "Create IP Range",
    "editTitle": "Edit IP Range",
    "required": "Required.",
    "invalidIp": "Invalid IP Address.",
    "deleteSuccess": "Successfully deleted IP ranges.",
    "updateSuccess": "Successfully updated IP range.",
    "createSuccess": "Successfully created IP range."
  },
  "de": {
    "ipranges": "IP Bereiche",
    "deleteIpranges": "Einen IP Bereich löschen | {0} IP Bereiche löschen",
    "createIprange": "IP Bereich erstellen",
    "id": "ID",
    "startIp": "Start IP",
    "endIp": "End IP",
    "group": "Gruppe",
    "deleteTitle": "Diesen IP Bereich löschen? | Diese {0} IP Bereiche löschen?",
    "createTitle": "IP Bereich erstellen",
    "editTitle": "IP Bereich bearbeiten",
    "required": "Benötigt.",
    "invalidIp": "Ungültige IP Adresse.",
    "deleteSuccess": "IP Bereiche erfolgreich gelöscht.",
    "updateSuccess": "IP Bereich erfolgreich aktualisiert.",
    "createSuccess": "IP Bereich erfolgreich erstellen."
  }
}
</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" :background-color="tabsColor" :slider-color="tabsSliderColor">
              <v-tab><v-icon class="tabbar-tabicon">settings_ethernet</v-icon>{{ $t('ipranges') }}</v-tab>
          </v-tabs>
        </v-card>
        <v-tabs-items v-model="tabs" style="padding-bottom: 20px">
          <v-tab-item>
            <v-subheader>{{ $t('ipranges') }}</v-subheader>
            <v-card>
              <data-table v-model="selected" :headers="headers" :items="ipranges" @dblclick="dialog = { show: true, type: 'edit', iprange: { ...$event } }" min-width="840px" copy-button>
                <template #group="row">
                  <v-btn small text :to="{ name: 'GroupModule.group', params: { id: row.item.groupId } }" class="ma-0" style="text-transform: none;">
                    {{ row.item.group.name }}
                    <v-icon small class="ml-2">open_in_new</v-icon>
                  </v-btn>
                </template>
                <template #actions="row">
                  <div style="text-align: right">
                    <v-btn text icon @click.stop="dialog = { show: true, type: 'edit', iprange: { ...row.item } }" class="ma-0"><v-icon color="primary">edit</v-icon></v-btn>
                  </div>
                </template>
              </data-table>
            </v-card>
            <div class="text-right">
              <v-btn text color="error" @click="dialog = { show: true, type: 'delete' }" :disabled="selected.length === 0" class="ma-2">
                <v-icon left>delete</v-icon>{{ $tc('deleteIpranges', selected.length, [selected.length]) }}
              </v-btn>
              <v-btn text color="success" @click="dialog = { show: true, type: 'create', iprange: {} }" class="ma-2">
                <v-icon left>create</v-icon>{{ $t('createIprange') }}
              </v-btn>
            </div>
          </v-tab-item>
        </v-tabs-items>
      </v-flex>
    </v-layout>

    <v-dialog
      v-model="dialog.show"
      :max-width="dialog.type === 'delete' ? '500px' : '500px'"
      scrollable
      :persistent="dialog.type !== 'delete'"
      :fullscreen="$vuetify.breakpoint.smAndDown"
    >
      <v-card>
        <v-card-title class="elevation-3">
          <div class="headline">{{ dialogTitle }}</div>
        </v-card-title>
        <v-card-text style="height: 100%">

          <div v-if="dialog.type === 'delete'" style="padding: 14px;">
            <RecycleScroller
              :items="selected"
              :item-size="24"
              page-mode
            >
              <div slot-scope="{ item }">[{{ item.id }}] {{ item.startIp }} - {{ item.endIp }}, {{ item.group.name }}</div>
            </RecycleScroller>
          </div>

          <v-form v-else ref="form" style="padding: 14px;">
            <select-box :rules="[rules.group]" prepend-icon="category" :label="$t('group')" v-model="dialog.iprange.selectedGroup" :items="groupList" single-select></select-box>
            <v-text-field :rules="[rules.ip]" validate-on-blur prepend-icon="vertical_align_top" :label="$t('startIp')" color="primary" v-model="dialog.iprange.startIp"></v-text-field>
            <v-text-field :rules="[rules.ip]" validate-on-blur prepend-icon="vertical_align_bottom" :label="$t('endIp')" color="primary" v-model="dialog.iprange.endIp"></v-text-field>
          </v-form>

        </v-card-text>

        <v-divider></v-divider>

        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn text="flat" @click="dialog.show = false">{{ $t('cancel') }}</v-btn>
          <v-btn :color="dialogColor" @click="submitAction">{{ dialogButtonText }}</v-btn>
        </v-card-actions>
      </v-card>

    </v-dialog>
  </v-container>
</template>

<script>
import SelectBox from '@/components/SelectBox'
import DataTable from '@/components/DataTable'
import { mapState, mapGetters } from 'vuex'

export default {
  name: 'IprangeModule',
  components: {
    SelectBox,
    DataTable
  },
  data () {
    return {
      tabs: 0,
      ipranges: [],
      selected: [],
      dialog: { show: false, type: null, iprange: {} }
    }
  },
  computed: {
    ...mapGetters(['tabsDark', 'tabsColor', 'tabsSliderColor']),
    ...mapState('groups', ['groupList']),
    headers () {
      return [
        { key: 'id', text: this.$t('id'), width: '50px', sortType: 'number' },
        { key: 'startIp', text: this.$t('startIp'), width: '120px', sortType: 'ipv4' },
        { key: 'endIp', text: this.$t('endIp'), width: '140px', sortType: 'ipv4' },
        { key: 'group', text: this.$t('group'), copyKey: 'groupName', sortKey: 'groupName' },
        { key: 'actions', width: '60px' }
      ]
    },
    rules () {
      return {
        group: value => value.length > 0 || this.$t('required'),
        ip: value => {
          if (!value) return this.$t('required')
          value = value.split('.')
          if (value.length !== 4) return this.$t('invalidIp')
          for (let i in value) {
            if (value[i] < 0 || value[i] > 255) return this.$t('invalidIp')
          }
          return true
        }
      }
    },
    dialogColor () {
      if (this.dialog.type === 'delete') return 'error'
      if (this.dialog.type === 'create') return 'success'
      return 'primary'
    },
    dialogTitle () {
      if (this.dialog.type === 'delete') return this.$tc('deleteTitle', this.selected.length, [this.selected.length])
      if (this.dialog.type === 'create') return this.$t('createTitle')
      return this.$t('editTitle')
    },
    dialogButtonText () {
      if (this.dialog.type === 'delete') return this.$t('delete')
      if (this.dialog.type === 'create') return this.$t('create')
      return this.$t('save')
    }
  },
  watch: {
    dialog (value) {
      if (this.$refs.form) this.$refs.form.resetValidation()
      if (value.type === 'edit') this.$set(this.dialog.iprange, 'selectedGroup', [this.dialog.iprange.group])
    }
  },
  methods: {
    async submitAction () {
      let text = ''
      if (this.dialog.type === 'delete') {
        await this.$http.post('/api/ipranges/?delete', {
          ids: this.selected.map(x => x.id)
        })
        text = this.$t('deleteSuccess')
      } else {
        if (this.$refs.form && !this.$refs.form.validate()) return
        let url = '/api/ipranges'
        if (this.dialog.iprange.id) {
          url += '/' + this.dialog.iprange.id
          text = this.$t('updateSuccess')
        } else {
          text = this.$t('createSuccess')
        }
        await this.$http.post(url, {
          startIp: this.dialog.iprange.startIp,
          endIp: this.dialog.iprange.endIp,
          groupId: this.dialog.iprange.selectedGroup[0].id
        })
      }
      this.dialog.show = false
      this.$snackbar({ text, color: 'success' })
      this.loadIpranges()
    },
    loadIpranges () {
      this.$http.get('/api/ipranges').then(response => {
        response.data.forEach(iprange => {
          iprange.groupName = iprange.group.name
        })
        this.ipranges = Object.freeze(response.data)
      })
    }
  },
  created () {
    this.$store.dispatch('groups/loadGroupList')
    this.loadIpranges()
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>