summaryrefslogblamecommitdiffstats
path: root/webapp/src/components/GroupModuleGroupInfo.vue
blob: 141fd135ed36b1b0b188d8260716024b6eeb35c2 (plain) (tree)
























































                                                                                                                                                
                                                                                                           









































































                                                                                                                                                                  
                                                                                                                                          

                                                              
                                                                                                                                          



















































































































                                                                                                                                              
















































































                                                                 
<i18n>
{
  "en": {
    "info": "Info",
    "groups": "Groups",
    "subgroups": "Subgroups",
    "clients": "Clients",
    "name": "Name",
    "description": "Description",
    "ipranges": "IP Ranges",
    "config": "iPXE Config",
    "parents": "Parents",
    "startIp": "Start IP",
    "endIp": "End IP",
    "more": "more"
  },
  "de": {
    "info": "Info",
    "groups": "Groups",
    "subgroups": "Untergruppen",
    "clients": "Clients",
    "name": "Name",
    "description": "Beschreibung",
    "ipranges": "IP Bereiche",
    "config": "iPXE Konfiguration",
    "parents": "Übergruppen",
    "startIp": "Start IP",
    "endIp": "End IP",
    "more": "mehr"
  }
}
</i18n>

<template>
  <div>
    <v-divider></v-divider>
    <v-card-text>
      <v-layout wrap>
        <v-flex lg4 sm6 xs12 order-lg1 order-xs2>
          <v-layout column>
            <v-flex>
              <div class="info-box">
                <div class="body-2 info-heading"><v-icon>label</v-icon><span>{{ $t('name') }}</span></div>
                <div class="info-text">
                  <v-text-field v-if="editMode" class="info-input" color="primary" v-model="info.name" hide-details tabindex="1"></v-text-field>
                  <div v-else>{{ group.name || '-' }}</div>
                </div>
              </div>
            </v-flex>
            <v-flex>
              <div class="info-box">
                <div class="body-2 info-heading"><v-icon>device_hub</v-icon><span>{{ $t('parents') }}</span></div>
                <div class="info-text">
                  <select-box v-if="editMode" v-model="parents" :items="groupList" class="info-input" hide-details></select-box>
                  <div v-else class="chip-container non-selectable">
                    <v-tooltip v-for="parent in firstParents" :key="parent.id" top open-delay="800">
                      <template #activator="{ on }">
                        <v-chip v-on="on" small style="width: calc(50% - 8px)" @click="openParent(parent)">
                          <span class="chip-text">{{ parent.name || parent.id }}</span>
                        </v-chip>
                      </template>
                      <span>{{ parent.name || parent.id }}</span>
                    </v-tooltip>
                    <span v-if="group.parents && group.parents.length > 5" class="and-more">+ {{ group.parents.length - 5 }} {{ $t('more') }}</span>
                    <span v-else-if="group.parents === undefined || group.parents.length === 0">-</span>
                  </div>
                </div>
              </div>
            </v-flex>
            <v-flex>
              <div class="info-box">
                <div class="body-2 info-heading"><v-icon>list</v-icon><span>{{ $t('config') }}</span></div>
                <div class="info-text">
                  <v-select v-if="editMode"
                    class="info-input"
                    clearable
                    item-text="name"
                    item-value="id"
                    :menu-props="{ offsetY: '' }"
                    hide-details
                    color="primary"
                    v-model="info.configId"
                    :items="configList"
                  ></v-select>
                  <div v-else>{{ group.config ? (group.config.name || group.config.id) : '-' }}</div>
                </div>
              </div>
            </v-flex>
          </v-layout>
        </v-flex>
        <v-flex lg4 sm6 xs12 order-lg2 order-xs3>
          <div class="info-box">
            <div class="body-2 info-heading"><v-icon>description</v-icon><span>{{ $t('description') }}</span></div>
            <div class="info-text">
              <v-textarea v-if="editMode" class="info-input" rows="1" auto-grow hide-details color="primary" v-model="info.description" tabindex="2"></v-textarea>
              <div v-else style="white-space: pre-wrap;">{{ group.description || '-' }}</div>
            </div>
          </div>

          <div class="info-box">
            <div class="body-2 info-heading"><v-icon>settings_ethernet</v-icon><span>{{ $t('ipranges') }}</span></div>
            <div class="info-text">
              <div v-if="editMode">
                <div v-for="(iprange, index) in ipranges" :key="index">
                  <div class="iprange">
                  <v-btn class="iprange-button" small icon @click="removeIprange(index)"><v-icon>remove</v-icon></v-btn>
                  <v-text-field solo flat hide-details :label="$t('startIp')" color="primary" v-model="iprange.startIp" single-line></v-text-field>
                  <span class="ip-seperator">-</span>
                  <v-text-field solo flat hide-details :label="$t('endIp')" color="primary" v-model="iprange.endIp" single-line></v-text-field>
                  </div>
                  <v-divider></v-divider>
                </div>
                <div class="iprange-add-wrapper">
                  <v-btn class="iprange-button" small icon @click="addIprange"><v-icon>add</v-icon></v-btn>
                </div>
              </div>
              <div v-else>
                <table>
                  <tr v-for="(iprange, index) in group.ipranges" :key="index">
                    <td class="text-xs-right">{{ iprange.startIp }}</td>
                    <td class="ip-seperator">-</td>
                    <td>{{ iprange.endIp }}</td>
                  </tr>
                </table>
                <div v-if="group.ipranges && group.ipranges.length === 0">-</div>
              </div>
            </div>
          </div>
        </v-flex>
        <v-flex lg4 xs12 order-lg3 order-xs1 class="text-xs-right">
          <div class="info-box">
            <div v-if="!editMode">
              <v-btn color="error" flat @click="deleteGroup" class="info-buttons tutorial-element label-left" style="--label-number: '2'">
                <v-icon left>delete</v-icon>{{ $t('delete') }}
              </v-btn>
              <v-btn color="primary" flat @click="editInfo" class="info-buttons tutorial-element label-right" style="--label-number: '1'">
                <v-icon left>create</v-icon>{{ $t('edit') }}
              </v-btn>
            </div>
            <div v-else>
              <v-btn color="primary" flat @click="cancelEdit" class="info-buttons">{{ $t('cancel') }}</v-btn>
              <v-btn color="primary" @click="saveData" class="info-buttons" tabindex="3">
                <v-icon left>save</v-icon>{{ $t('save') }}
              </v-btn>
            </div>
          </div>
        </v-flex>
      </v-layout>
    </v-card-text>
  </div>
</template>

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

export default {
  name: 'GroupModuleGroupInfo',
  props: {
    group: {
      type: Object,
      default: () => {}
    },
    tabIndex: {
      type: Number
    }
  },
  components: {
    SelectBox
  },
  data () {
    return {
      editMode: false,
      info: {
        name: '',
        description: '',
        configId: null
      },
      parents: [],
      ipranges: []
    }
  },
  computed: {
    ...mapState('groups', ['groupList', 'configList']),
    firstParents () {
      return this.group.parents ? this.group.parents.slice(0, 5) : []
    }
  },
  watch: {
    group (newValue, oldValue) {
      if (newValue.id === 'create') this.editInfo()
      else if (newValue.id !== oldValue.id) this.editMode = false
    }
  },
  methods: {
    ...mapMutations('groups', ['setDialog', 'setActiveTab', 'adjustTabSlider', 'deleteFromTabChain']),
    removeIprange (index) {
      this.ipranges.splice(index, 1)
    },
    addIprange () {
      this.ipranges.push({ startIp: '', endIp: '' })
    },
    editInfo () {
      this.editMode = true
      this.info.name = this.group.name
      this.info.description = this.group.description
      this.info.configId = this.group.configId
      this.parents = this.group.parents ? this.group.parents.slice(0) : []
      this.ipranges = this.group.ipranges ? this.group.ipranges.slice(0) : []
    },
    cancelEdit () {
      this.editMode = false
      if (this.group.id === 'create') {
        this.deleteFromTabChain({ index: this.tabIndex, count: 1 })
        this.setActiveTab(this.tabIndex - 1)
      }
    },
    saveData () {
      this.info.configId = this.info.configId === undefined ? null : this.info.configId
      this.ipranges = this.ipranges.filter(iprange => iprange.startIp && iprange.endIp)
      this.adjustTabSlider()
      this.$store.dispatch('groups/saveGroup', {
        id: this.group.id,
        data: this.info,
        parents: this.parents,
        ipranges: this.ipranges,
        tabIndex: this.tabIndex,
        callback: this.updateUrl
      })
      this.editMode = false
    },
    deleteGroup () {
      this.setDialog({ show: true, info: { action: 'delete', type: 'group', selected: [this.group] } })
    },
    updateUrl (id) {
      this.$router.replace({
        name: 'GroupModule.group',
        params: { id, noReload: true }
      })
    },
    openParent (parent) {
      this.$store.dispatch('groups/loadGroup', { id: parent.id, name: parent.name, tabIndex: this.tabIndex, asParent: true, switchTab: true })
    }
  },
  created () {
    if (this.group.id === 'create') this.editInfo()
  }
}
</script>

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

.iprange {
  display: flex;
  align-items: center;
  min-height: 34px;
}

.iprange-add-wrapper {
  height: 32px;
  display: flex;
  align-items: center;
}

.iprange >>> .v-text-field.v-text-field--solo .v-input__control {
  min-height: 32px;
}

.iprange >>> .v-label, .iprange >>> input {
  font-size: 14px;
}

.chip-container {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}

.chip-container >>> .v-chip__content {
  width: 100%;
  cursor: pointer;
}

.chip-text {
  overflow: hidden;
  text-overflow: ellipsis;
}

.and-more {
  font-size: 13px;
  display: flex;
  align-items: center;
  margin: 4px 17px;
}

.ip-seperator {
  padding: 0 10px;
}

.iprange-button {
  margin: 0 8px 0 0;
}

.info-box {
  padding: 20px;
}

.info-input {
  margin: 0;
  padding: 0 0 1px 0;
  overflow: hidden;
}

.info-heading {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.info-heading > span {
  margin-left: 10px;
}

.info-text {
  overflow-x: auto;
  margin-left: 34px;
  font-family: 'Roboto Mono';
  min-height: 34px;
  display: flex;
  align-items: center;
}
</style>