Add SNMP alerts page and test hooks

This page will be included in Configuration section of the primary
navigation. The user will be able to delete and add alert destination.

Change-Id: I396d19a54ea11724f2c5aec67e20ba9abff947d3
Signed-off-by: Konstantin Aladyshev <aladyshev22@gmail.com>
diff --git a/src/components/AppNavigation/AppNavigationMixin.js b/src/components/AppNavigation/AppNavigationMixin.js
index 6123098..b33b24b 100644
--- a/src/components/AppNavigation/AppNavigationMixin.js
+++ b/src/components/AppNavigation/AppNavigationMixin.js
@@ -136,6 +136,11 @@
               label: this.$t('appNavigation.powerRestorePolicy'),
               route: '/settings/power-restore-policy',
             },
+            {
+              id: 'snmp-alerts',
+              label: this.$t('appNavigation.snmpAlerts'),
+              route: '/settings/snmp-alerts',
+            },
           ],
         },
         {
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index ff10769..18bb7c6 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -130,6 +130,7 @@
         "serverPowerOperations": "@:appPageTitle.serverPowerOperations",
         "certificates": "@:appPageTitle.certificates",
         "virtualMedia": "@:appPageTitle.virtualMedia",
+        "snmpAlerts": "@:appPageTitle.snmpAlerts",
         "power": "@:appPageTitle.power",
         "keyClear": "@:appPageTitle.keyClear"
     },
@@ -159,6 +160,7 @@
         "serialOverLan": "Serial over LAN (SOL) console",
         "serverPowerOperations": "Server power operations",
         "certificates": "Certificates",
+        "snmpAlerts": "SNMP Alerts",
         "virtualMedia": "Virtual media",
         "keyClear": "Key clear"
     },
@@ -895,6 +897,32 @@
             "successSaveSettings": "Successfully saved settings."
         }
     },
+    "pageSnmpAlerts": {
+        "addDestination": "Add destination",
+        "deleteDestination": "Delete destination | Delete destinations",
+        "pageDescription": "Set the Simple Network Management Protocol (SNMP) traps with an IP address and a port.",
+        "modal": {
+            "addSnmpDestinationTitle": "Add SNMP alert destination",
+            "batchDeleteConfirmMessage": "Are you sure you want to delete the SNMP alert destination? This action cannot be undone. | Are you sure you want to delete %{count} SNMP alert destinations? This action cannot be undone.",
+            "deleteConfirmMessage": "Are you sure you want to delete the SNMP alert destination? This action cannot be undone.",
+            "deleteSnmpDestinationTitle": "Delete SNMP alert destination | Delete SNMP alert destinations",
+            "ipaddress": "IP Address",
+            "port": "Port"
+        },
+        "table": {
+            "ipaddress": "IP Address",
+            "port": "Port"
+        },
+        "toast": {
+            "errorAddDestination": "Error in adding SNMP alert destination",
+            "errorBatchDelete": "Error in deleting SNMP alert destination. | Error in deleting SNMP alert destinations.",
+            "errorDeleteDestination": "Error deleting SNMP alert destination.",
+            "errorLoadSnmpDetails": "Error loading SNMP alert details.",
+            "successAddDestination": "Successfully added SNMP alert destination.",
+            "successBatchDelete": "Successfully deleted SNMP alert destination. | Successfully deleted %{count} SNMP alert destinations.",
+            "successDeleteDestination": "Successfully deleted SNMP alert destination."
+        }
+    },
     "pageCertificates": {
         "addNewCertificate": "Add new certificate",
         "caCertificate": "CA Certificate",
diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json
index d6c8275..f01bac7 100644
--- a/src/locales/ru-RU.json
+++ b/src/locales/ru-RU.json
@@ -130,6 +130,7 @@
         "serverPowerOperations": "@:appPageTitle.serverPowerOperations",
         "certificates": "@:appPageTitle.certificates",
         "virtualMedia": "@:appPageTitle.virtualMedia",
+        "snmpAlerts": "@:appPageTitle.snmpAlerts",
         "power": "@:appPageTitle.power",
         "keyClear": "@:appPageTitle.keyClear"
     },
@@ -159,6 +160,7 @@
         "serialOverLan": "Консоль Serial over LAN (SOL)",
         "serverPowerOperations": "Управление питанием сервера",
         "certificates": "Сертификаты",
+        "snmpAlerts": "SNMP оповещения",
         "virtualMedia": "Виртуальные носители",
         "keyClear": "Удаление ключей"
     },
@@ -895,6 +897,32 @@
             "successSaveSettings": "Успешное сохранение настроек."
         }
     },
+    "pageSnmpAlerts": {
+        "addDestination": "Добавить получателя",
+        "deleteDestination": "Удалить получателя | Удалить получателей",
+        "pageDescription": "Настройка IP адреса и порта для Simple Network Management Protocol (SNMP) ловушек.",
+        "modal": {
+            "addSnmpDestinationTitle": "Добавить получателя SNMP оповещения",
+            "batchDeleteConfirmMessage": "Вы уверены, что хотите удалить получателя SNMP оповещения? Действие не может быть отменено. | Вы уверены, что хотите удалить %{count} получателей SNMP оповещений? Действие не может быть отменено.",
+            "deleteConfirmMessage": "Вы уверены, что хотите удалить получателя SNMP оповещения? Действие не может быть отменено.",
+            "deleteSnmpDestinationTitle": "Удаление получателя SNMP оповещения | Удаление получателей SNMP оповещений",
+            "ipaddress": "IP адрес",
+            "port": "Порт"
+        },
+        "table": {
+            "ipaddress": "IP адрес",
+            "port": "Порт"
+        },
+        "toast": {
+            "errorAddDestination": "Ошибка добавления получателя SNMP оповещения",
+            "errorBatchDelete": "Ошибка удаления получателя SNMP оповещения. | Ошибка удаления получателей SNMP оповещений.",
+            "errorDeleteDestination": "Ошибка удаления получателя SNMP оповещения.",
+            "errorLoadSnmpDetails": "Ошибка загрузки информации о получателе SNMP оповещения.",
+            "successAddDestination": "Успешное добавление получателя SNMP оповещения.",
+            "successBatchDelete": "Успешное удаление получателя SNMP оповещения. | Успешное удаление %{count} получателей SNMP оповещений.",
+            "successDeleteDestination": "Успешное удаление получателя SNMP оповещения."
+        }
+    },
     "pageCertificates": {
         "addNewCertificate": "Добавить новый сертификат",
         "caCertificate": "Сертификат CA",
diff --git a/src/router/routes.js b/src/router/routes.js
index 4cf8d72..eb376aa 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -29,6 +29,7 @@
 import Certificates from '@/views/SecurityAndAccess/Certificates';
 import VirtualMedia from '@/views/Operations/VirtualMedia';
 import Power from '@/views/ResourceManagement/Power';
+import SnmpAlerts from '@/views/Settings/SnmpAlerts';
 import i18n from '@/i18n';
 
 const roles = {
@@ -191,6 +192,14 @@
         },
       },
       {
+        path: '/settings/snmp-alerts',
+        name: 'snmp-alerts',
+        component: SnmpAlerts,
+        meta: {
+          title: i18n.t('appPageTitle.snmpAlerts'),
+        },
+      },
+      {
         path: '/operations/factory-reset',
         name: 'factory-reset',
         component: FactoryReset,
diff --git a/src/store/index.js b/src/store/index.js
index c0b7894..8b1ed07 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -28,6 +28,7 @@
 import PostCodeLogsStore from './modules/Logs/PostCodeLogsStore';
 import PoliciesStore from './modules/SecurityAndAccess/PoliciesStore';
 import FactoryResetStore from './modules/Operations/FactoryResetStore';
+import SnmpAlertsStore from './modules/Settings/SnmpAlertsStore';
 import KeyClearStore from './modules/Operations/KeyClearStore';
 
 import WebSocketPlugin from './plugins/WebSocketPlugin';
@@ -58,6 +59,7 @@
     dumps: DumpsStore,
     sensors: SensorsStore,
     serverLed: ServerLedStore,
+    snmpAlerts: SnmpAlertsStore,
     certificates: CertificatesStore,
     system: SystemStore,
     memory: MemoryStore,
diff --git a/src/store/modules/Settings/SnmpAlertsStore.js b/src/store/modules/Settings/SnmpAlertsStore.js
new file mode 100644
index 0000000..f945ee3
--- /dev/null
+++ b/src/store/modules/Settings/SnmpAlertsStore.js
@@ -0,0 +1,121 @@
+import api, { getResponseCount } from '@/store/api';
+import i18n from '@/i18n';
+
+const SnmpAlertsStore = {
+  namespaced: true,
+  state: {
+    allSnmpDetails: [],
+  },
+  getters: {
+    allSnmpDetails(state) {
+      return state.allSnmpDetails;
+    },
+  },
+  mutations: {
+    setSnmpDetails(state, allSnmpDetails) {
+      state.allSnmpDetails = allSnmpDetails;
+    },
+  },
+  actions: {
+    async getSnmpAlertUrl() {
+      return await api
+        .get('/redfish/v1/')
+        .then((response) => api.get(response.data.EventService['@odata.id']))
+        .then((response) => api.get(response.data.Subscriptions['@odata.id']))
+        .then((response) => response.data['@odata.id'])
+        .catch((error) => console.log('Error', error));
+    },
+    async getSnmpDetails({ commit, dispatch }) {
+      const snmpAlertUrl = await dispatch('getSnmpAlertUrl');
+      return await api
+        .get(snmpAlertUrl)
+        .then((response) =>
+          response.data.Members.map((user) => user['@odata.id'])
+        )
+        .then((userIds) => api.all(userIds.map((user) => api.get(user))))
+        .then((users) => {
+          const snmpDetailsData = users.map((user) => user.data);
+          commit('setSnmpDetails', snmpDetailsData);
+        })
+        .catch((error) => {
+          console.log(error);
+          const message = i18n.t('pageSnmpAlerts.toast.errorLoadSnmpDetails');
+          throw new Error(message);
+        });
+    },
+    async deleteDestination({ dispatch }, id) {
+      const snmpAlertUrl = await dispatch('getSnmpAlertUrl');
+      return await api
+        .delete(`${snmpAlertUrl}/${id}`)
+        .then(() => dispatch('getSnmpDetails'))
+        .then(() =>
+          i18n.t('pageSnmpAlerts.toast.successDeleteDestination', {
+            id,
+          })
+        )
+        .catch((error) => {
+          console.log(error);
+          const message = i18n.t(
+            'pageSnmpAlerts.toast.errorDeleteDestination',
+            {
+              id,
+            }
+          );
+          throw new Error(message);
+        });
+    },
+    async deleteMultipleDestinations({ dispatch }, destination) {
+      const snmpAlertUrl = await dispatch('getSnmpAlertUrl');
+      const promises = destination.map(({ id }) => {
+        return api.delete(`${snmpAlertUrl}/${id}`).catch((error) => {
+          console.log(error);
+          return error;
+        });
+      });
+      return await api
+        .all(promises)
+        .then((response) => {
+          dispatch('getSnmpDetails');
+          return response;
+        })
+        .then(
+          api.spread((...responses) => {
+            const { successCount, errorCount } = getResponseCount(responses);
+            let toastMessages = [];
+
+            if (successCount) {
+              const message = i18n.tc(
+                'pageSnmpAlerts.toast.successBatchDelete',
+                successCount
+              );
+              toastMessages.push({ type: 'success', message });
+            }
+
+            if (errorCount) {
+              const message = i18n.tc(
+                'pageSnmpAlerts.toast.errorBatchDelete',
+                errorCount
+              );
+              toastMessages.push({ type: 'error', message });
+            }
+
+            return toastMessages;
+          })
+        );
+    },
+    async addDestination({ dispatch }, { data }) {
+      const snmpAlertUrl = await dispatch('getSnmpAlertUrl');
+      return await api
+        .post(snmpAlertUrl, data)
+        .then(() => dispatch('getSnmpDetails'))
+        .then(() => i18n.t('pageSnmpAlerts.toast.successAddDestination'))
+        .catch((error) => {
+          console.log(error);
+          const message = i18n.t('pageSnmpAlerts.toast.errorAddDestination');
+          throw new Error(message);
+        });
+    },
+  },
+};
+
+export default SnmpAlertsStore;
diff --git a/src/views/Settings/SnmpAlerts/ModalAddDestination.vue b/src/views/Settings/SnmpAlerts/ModalAddDestination.vue
new file mode 100644
index 0000000..9637652
--- /dev/null
+++ b/src/views/Settings/SnmpAlerts/ModalAddDestination.vue
@@ -0,0 +1,145 @@
+<template>
+  <b-modal id="add-destination" ref="modal" @ok="onOk" @hidden="resetForm">
+    <template #modal-title>
+      {{ $t('pageSnmpAlerts.modal.addSnmpDestinationTitle') }}
+    </template>
+    <b-form id="form-destination">
+      <b-container>
+        <b-row>
+          <b-col sm="6">
+            <!-- Add new SNMP alert destination type -->
+            <b-form-group
+              :label="$t('pageSnmpAlerts.modal.ipaddress')"
+              label-for="ip-address"
+            >
+              <b-form-input
+                id="ip-Address"
+                v-model="form.ipAddress"
+                :state="getValidationState($v.form.ipAddress)"
+                data-test-id="snmpAlerts-input-ipAddress"
+                type="text"
+                @blur="$v.form.ipAddress.$touch()"
+              />
+
+              <b-form-invalid-feedback role="alert">
+                <template v-if="!$v.form.ipAddress.required">
+                  {{ $t('global.form.fieldRequired') }}
+                </template>
+                <template v-if="!$v.form.ipAddress.ipAddress">
+                  {{ $t('global.form.invalidFormat') }}
+                </template>
+              </b-form-invalid-feedback>
+            </b-form-group>
+          </b-col>
+          <b-col>
+            <b-form-group label-for="port">
+              <template #label>
+                {{ $t('pageSnmpAlerts.modal.port') }} -
+                <span class="form-text d-inline">
+                  {{ $t('global.form.optional') }}
+                </span>
+              </template>
+              <b-form-input
+                id="port"
+                v-model="form.port"
+                type="text"
+                :state="getValidationState($v.form.port)"
+                data-test-id="snmpAlerts-input-port"
+                @blur="$v.form.port.$touch()"
+              />
+              <b-form-invalid-feedback role="alert">
+                <template
+                  v-if="!$v.form.port.minLength || !$v.form.port.maxLength"
+                >
+                  {{
+                    $t('global.form.valueMustBeBetween', {
+                      min: 0,
+                      max: 65535,
+                    })
+                  }}
+                </template>
+              </b-form-invalid-feedback>
+            </b-form-group>
+          </b-col>
+        </b-row>
+      </b-container>
+    </b-form>
+    <template #modal-footer="{ cancel }">
+      <b-button variant="secondary" @click="cancel()">
+        {{ $t('global.action.cancel') }}
+      </b-button>
+      <b-button
+        form="form-user"
+        type="submit"
+        variant="primary"
+        data-test-id="snmpAlerts-button-ok"
+        @click="onOk"
+      >
+        {{ $t('pageSnmpAlerts.addDestination') }}
+      </b-button>
+    </template>
+  </b-modal>
+</template>
+
+<script>
+import {
+  required,
+  ipAddress,
+  minValue,
+  maxValue,
+} from 'vuelidate/lib/validators';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+
+export default {
+  mixins: [VuelidateMixin],
+  data() {
+    return {
+      form: {
+        ipaddress: null,
+        port: null,
+      },
+    };
+  },
+  validations() {
+    return {
+      form: {
+        ipAddress: {
+          required,
+          ipAddress,
+        },
+        port: {
+          minValue: minValue(0),
+          maxValue: maxValue(65535),
+        },
+      },
+    };
+  },
+  methods: {
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+      this.$emit('ok', {
+        ipAddress: this.form.ipAddress,
+        port: this.form.port,
+      });
+      this.closeModal();
+    },
+    closeModal() {
+      this.$nextTick(() => {
+        this.$refs.modal.hide();
+      });
+    },
+    resetForm() {
+      this.form.ipAddress = '';
+      this.form.port = '';
+      this.$v.$reset();
+      this.$emit('hidden');
+    },
+    onOk(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      this.handleSubmit();
+    },
+  },
+};
+</script>
diff --git a/src/views/Settings/SnmpAlerts/SnmpAlerts.vue b/src/views/Settings/SnmpAlerts/SnmpAlerts.vue
new file mode 100644
index 0000000..8a9b8e7
--- /dev/null
+++ b/src/views/Settings/SnmpAlerts/SnmpAlerts.vue
@@ -0,0 +1,274 @@
+<template>
+  <b-container fluid="xl">
+    <page-title :description="$t('pageSnmpAlerts.pageDescription')" />
+    <b-row>
+      <b-col xl="9" class="text-right">
+        <b-button variant="primary" @click="initModalAddDestination">
+          <icon-add />
+          {{ $t('pageSnmpAlerts.addDestination') }}
+        </b-button>
+      </b-col>
+    </b-row>
+    <b-row>
+      <b-col xl="9">
+        <table-toolbar
+          ref="toolbar"
+          :selected-items-count="selectedRows.length"
+          :actions="tableToolbarActions"
+          @clear-selected="clearSelectedRows($refs.table)"
+          @batch-action="onBatchAction"
+        />
+        <b-table
+          ref="table"
+          responsive="md"
+          selectable
+          show-empty
+          no-select-on-click
+          hover
+          :fields="fields"
+          :items="tableItems"
+          :empty-text="$t('global.table.emptyMessage')"
+          @row-selected="onRowSelected($event, tableItems.length)"
+        >
+          <!-- Checkbox column -->
+          <template #head(checkbox)>
+            <b-form-checkbox
+              v-model="tableHeaderCheckboxModel"
+              data-test-id="snmpAlerts-checkbox-selectAll"
+              :indeterminate="tableHeaderCheckboxIndeterminate"
+              @change="onChangeHeaderCheckbox($refs.table)"
+            >
+              <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+            </b-form-checkbox>
+          </template>
+          <template #cell(checkbox)="row">
+            <b-form-checkbox
+              v-model="row.rowSelected"
+              :data-test-id="`snmpAlerts-checkbox-selectRow-${row.index}`"
+              @change="toggleSelectRow($refs.table, row.index)"
+            >
+              <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+            </b-form-checkbox>
+          </template>
+
+          <!-- table actions column -->
+          <template #cell(actions)="{ item }">
+            <table-row-action
+              v-for="(action, index) in item.actions"
+              :key="index"
+              :value="action.value"
+              :enabled="action.enabled"
+              :title="action.title"
+              :data-test-id="`snmpAlerts-button-deleteRow-${item.index}`"
+              @click-table-action="onTableRowAction($event, item)"
+            >
+              <template #icon>
+                <icon-trashcan v-if="action.value === 'delete'" />
+              </template>
+            </table-row-action>
+          </template>
+        </b-table>
+      </b-col>
+    </b-row>
+    <!-- Modals -->
+    <modal-add-destination @ok="onModalOk" />
+  </b-container>
+</template>
+
+<script>
+import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+import ModalAddDestination from './ModalAddDestination';
+import PageTitle from '@/components/Global/PageTitle';
+import IconAdd from '@carbon/icons-vue/es/add--alt/20';
+import TableToolbar from '@/components/Global/TableToolbar';
+import TableRowAction from '@/components/Global/TableRowAction';
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+
+import BVTableSelectableMixin, {
+  selectedRows,
+  tableHeaderCheckboxModel,
+  tableHeaderCheckboxIndeterminate,
+} from '@/components/Mixins/BVTableSelectableMixin';
+export default {
+  name: 'SnmpAlerts',
+  components: {
+    PageTitle,
+    IconAdd,
+    TableToolbar,
+    IconTrashcan,
+    ModalAddDestination,
+    TableRowAction,
+  },
+  mixins: [BVTableSelectableMixin, BVToastMixin, LoadingBarMixin],
+  beforeRouteLeave(to, from, next) {
+    this.hideLoader();
+    next();
+  },
+  data() {
+    return {
+      fields: [
+        {
+          key: 'checkbox',
+        },
+        {
+          key: 'IP',
+          label: this.$t('pageSnmpAlerts.table.ipaddress'),
+        },
+        {
+          key: 'Port',
+          label: this.$t('pageSnmpAlerts.table.port'),
+        },
+        {
+          key: 'actions',
+          label: '',
+          tdClass: 'text-right text-nowrap',
+        },
+      ],
+      tableToolbarActions: [
+        {
+          value: 'delete',
+          label: this.$t('global.action.delete'),
+        },
+      ],
+      selectedRows: selectedRows,
+      tableHeaderCheckboxModel: tableHeaderCheckboxModel,
+      tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate,
+    };
+  },
+  computed: {
+    allSnmpDetails() {
+      return this.$store.getters['snmpAlerts/allSnmpDetails'];
+    },
+    tableItems() {
+      // transform destination data to table data
+      return this.allSnmpDetails.map((subscriptions) => {
+        const [destination, dataWithProtocol, dataWithoutProtocol] = [
+          subscriptions.Destination,
+          subscriptions.Destination.split('/')[2].split(':'),
+          subscriptions.Destination.split(':'),
+        ];
+        //condition to check if destination comes with protocol or not
+        const conditionForProtocolCheck = destination.includes('://');
+        const ip = conditionForProtocolCheck
+          ? dataWithProtocol[0]
+          : dataWithoutProtocol[0];
+        const port = conditionForProtocolCheck
+          ? dataWithProtocol[1]
+          : dataWithoutProtocol[1];
+        return {
+          IP: ip,
+          Port: port,
+          id: subscriptions.Id,
+          actions: [
+            {
+              value: 'delete',
+              enabled: true,
+              title: this.$tc('pageSnmpAlerts.deleteDestination'),
+            },
+          ],
+          ...subscriptions,
+        };
+      });
+    },
+  },
+  created() {
+    this.startLoader();
+    this.$store
+      .dispatch('snmpAlerts/getSnmpDetails')
+      .finally(() => this.endLoader());
+  },
+  methods: {
+    onModalOk({ ipAddress, port }) {
+      const protocolIpAddress = 'snmp://' + ipAddress;
+      const destination = port
+        ? protocolIpAddress + ':' + port
+        : protocolIpAddress;
+      const data = {
+        Destination: destination,
+        SubscriptionType: 'SNMPTrap',
+        Protocol: 'SNMPv2c',
+      };
+      this.startLoader();
+      this.$store
+        .dispatch('snmpAlerts/addDestination', { data })
+        .then((success) => this.successToast(success))
+        .catch(({ message }) => this.errorToast(message))
+        .finally(() => this.endLoader());
+    },
+    initModalAddDestination() {
+      this.$bvModal.show('add-destination');
+    },
+    initModalDeleteDestination(destination) {
+      this.$bvModal
+        .msgBoxConfirm(
+          this.$t('pageSnmpAlerts.modal.deleteConfirmMessage', {
+            destination: destination.id,
+          }),
+          {
+            title: this.$tc('pageSnmpAlerts.modal.deleteSnmpDestinationTitle'),
+            okTitle: this.$tc('pageSnmpAlerts.deleteDestination'),
+            cancelTitle: this.$t('global.action.cancel'),
+          }
+        )
+        .then((deleteConfirmed) => {
+          if (deleteConfirmed) {
+            this.deleteDestination(destination);
+          }
+        });
+    },
+    deleteDestination({ id }) {
+      this.startLoader();
+      this.$store
+        .dispatch('snmpAlerts/deleteDestination', id)
+        .then((success) => this.successToast(success))
+        .catch(({ message }) => this.errorToast(message))
+        .finally(() => this.endLoader());
+    },
+    onBatchAction(action) {
+      if (action === 'delete') {
+        this.$bvModal
+          .msgBoxConfirm(
+            this.$tc(
+              'pageSnmpAlerts.modal.batchDeleteConfirmMessage',
+              this.selectedRows.length
+            ),
+            {
+              title: this.$tc(
+                'pageSnmpAlerts.modal.deleteSnmpDestinationTitle',
+                this.selectedRows.length
+              ),
+              okTitle: this.$tc(
+                'pageSnmpAlerts.deleteDestination',
+                this.selectedRows.length
+              ),
+              cancelTitle: this.$t('global.action.cancel'),
+            }
+          )
+          .then((deleteConfirmed) => {
+            if (deleteConfirmed) {
+              this.startLoader();
+              this.$store
+                .dispatch(
+                  'snmpAlerts/deleteMultipleDestinations',
+                  this.selectedRows
+                )
+                .then((messages) => {
+                  messages.forEach(({ type, message }) => {
+                    if (type === 'success') this.successToast(message);
+                    if (type === 'error') this.errorToast(message);
+                  });
+                })
+                .finally(() => this.endLoader());
+            }
+          });
+      }
+    },
+    onTableRowAction(action, row) {
+      if (action === 'delete') {
+        this.initModalDeleteDestination(row);
+      }
+    },
+  },
+};
+</script>
diff --git a/src/views/Settings/SnmpAlerts/index.js b/src/views/Settings/SnmpAlerts/index.js
new file mode 100644
index 0000000..f27ed4a
--- /dev/null
+++ b/src/views/Settings/SnmpAlerts/index.js
@@ -0,0 +1,2 @@
+import SnmpAlerts from './SnmpAlerts.vue';
+export default SnmpAlerts;