Source: management-system/src/frontend/views/Machines.vue

<template>
  <div>
    <v-toolbar>
      <v-toolbar-title>Machines</v-toolbar-title>
      <v-spacer />
      <v-btn
        v-if="$can('manage', 'Machine')"
        color="primary"
        @click="addMachineDialog = true"
        :loading="addIsWaitingForBackend"
        >Add</v-btn
      >
    </v-toolbar>

    <confirmation
      v-if="tobeDeletedMachine.all == true"
      :title="'delete all saved machines?'"
      :text="confirmationText"
      continueButtonText="Delete all"
      continueButtonColor="error"
      :show="removeMachineDialog"
      maxWidth="350px"
      @cancel="removeMachineDialog = false"
      @continue="deleteAllSaved()"
    />
    <confirmation
      v-else
      :title="'delete the selected machine?'"
      :text="confirmationText"
      continueButtonText="Delete"
      continueButtonColor="error"
      :show="removeMachineDialog"
      @cancel="removeMachineDialog = false"
      @continue="deleteMachine()"
    />

    <!-- create one container for discovered and one for saved machines -->
    <v-container
      fluid
      v-for="(array, index) in [
        $store.getters['machineStore/discoveredMachines'],
        $store.getters['machineStore/savedMachines'],
      ]"
      :key="index"
    >
      <v-row justify="center" id="wrapper">
        <v-col class="text-center centered">
          <v-card>
            <v-subheader>{{ !index ? 'Discovered' : 'Saved' }}</v-subheader>
            <div v-if="index === 0">
              <MachineForm
                :show="addMachineDialog"
                @cancel="addMachineDialog = false"
                @add="addMachine"
              />
              <MachineForm
                :machine="editingMachine"
                :show="!!editMachineId"
                @cancel="editMachineId = ''"
                @update="updateMachine"
              />
            </div>
            <v-card-text>
              <v-data-table
                :headers="fields"
                :items="index === 0 ? connectedMachines(array) : array"
                hide-default-footer
              >
                <template v-slot:item="{ item }">
                  <tr
                    @click="toggleDisplayDetailed(item)"
                    style="text-align: center"
                    v-if="item.saved || item.status === 'CONNECTED'"
                  >
                    <td>{{ item.name }}</td>
                    <td>{{ item.hostname }}</td>
                    <td>
                      {{ item.ip }}
                      <svg v-if="item.status === 'CONNECTED'" width="15px" height="15px">
                        <circle cx="7.5" cy="10" r="5" fill="#0c0" />
                      </svg>
                      <svg
                        v-if="item.status !== 'CONNECTED' && item.saved"
                        width="15px"
                        height="15px"
                      >
                        <circle cx="7.5" cy="10" r="5" fill="#a00" />
                      </svg>
                    </td>
                    <!--td>{{ item.port }}</td-->
                    <td>{{ item.optionalName }}</td>
                    <td>{{ item.id }}</td>
                    <td>{{ item.description }}</td>
                    <td v-if="item.saved && $can('manage', 'Machine')">
                      <v-icon @click.stop="editMachineId = item.id" class="mx-0" color="primary">
                        mdi-pencil
                      </v-icon>
                      <v-icon
                        class="mx-0"
                        color="error"
                        @click.stop="openRemoveMachineDialog(false, item.id)"
                      >
                        mdi-delete
                      </v-icon>
                    </td>
                    <td v-if="!item.saved && $can('manage', 'Machine')">
                      <v-icon class="mx-0" @click.stop="setMachineSaved(item)" color="primary">
                        mdi-content-save
                      </v-icon>
                    </td>
                  </tr>
                  <tr>
                    <td
                      :colspan="fields.length"
                      v-if="elementInDisplayDetailed(item)"
                      class="detailed detailedBottom"
                    >
                      <v-card class="text-left my-2">
                        <v-card-text>
                          <div>
                            <span class="font-weight-medium">Machine: </span>
                            <a>{{ item.hostname }}</a>
                          </div>
                          <v-divider class="my-2" />
                          <v-card flat class="mb-2 ml-3">
                            <MachineInfo
                              :displayDetailed="displayDetailed"
                              @showConfig="configDialog = true"
                            />
                            <MachineConfig
                              :machine="detailedMachine"
                              :show="configDialog"
                              @close="configDialog = false"
                            />
                          </v-card>
                        </v-card-text>
                      </v-card>
                    </td>
                  </tr>
                </template>
              </v-data-table>
              <div v-if="!!index">
                <svg width="15px" height="15px">
                  <circle cx="7.5" cy="10" r="5" fill="#0c0" />
                </svg>
                <label>= connection established</label>
              </div>
            </v-card-text>
            <v-btn
              class="mb-3"
              color="error"
              small
              @click="openRemoveMachineDialog(true, 0)"
              v-if="!!index && $can('manage', 'Machine')"
            >
              Delete All
            </v-btn>
          </v-card>
        </v-col>
      </v-row>
    </v-container>
  </div>
</template>

<script>
import uuid from 'uuid';
import MachineForm from '@/frontend/components/machines/MachineForm.vue';
import MachineConfig from '@/frontend/components/machines/MachineConfig.vue';
import MachineInfo from '@/frontend/components/machines/MachineInfo.vue';
import { fields } from '@/frontend/helpers/machine-fields.js';
import confirmation from '@/frontend/components/universal/Confirmation.vue';
import { engineNetworkInterface } from '@/frontend/backend-api/index.js';

/**
 * @module views
 */
/**
 * @memberof module:views
 * @module Vue:Machines
 *
 * @vue-computed {Array} machines - list of machines
 * @vue-computed editingMachine
 * @vue-computed detailedMachine
 * @vue-computed fields
 */
export default {
  name: 'Machines',
  components: {
    MachineForm,
    MachineConfig,
    MachineInfo,
    confirmation,
  },
  computed: {
    machines() {
      return this.$store.getters['machineStore/machines'];
    },
    editingMachine() {
      return this.machines.find((machine) => machine.id === this.editMachineId);
    },
    detailedMachine() {
      return this.$store.getters['machineStore/machineById'](this.displayDetailed);
    },
    fields() {
      return fields;
    },
  },
  data() {
    return {
      /** */
      addMachineDialog: false,
      /** */
      configDialog: false,
      /** */
      removeMachineDialog: false,
      /** */
      editMachineId: '',
      /** */
      tobeDeletedMachine: {},
      /** */
      confirmationText: 'Do you want to continue?',
      /** */
      displayDetailed: null,
      /** */
      addIsWaitingForBackend: false,
    };
  },
  methods: {
    /** */
    async addMachine(machine) {
      this.addIsWaitingForBackend = true;
      await this.$store.dispatch('machineStore/add', {
        machine: {
          ...machine,
          id: uuid.v4(),
          saved: true,
        },
      });
      this.addIsWaitingForBackend = false;
      this.addMachineDialog = false;
    },
    /** */
    updateMachine(machine) {
      this.$store.dispatch('machineStore/update', {
        machine: { ...machine, saved: true },
      });
      this.editMachineId = '';
    },
    /** */
    openRemoveMachineDialog(all, id) {
      this.tobeDeletedMachine.all = all;
      this.tobeDeletedMachine.id = id;
      this.removeMachineDialog = true;
    },
    /**
     * deletes a machine from the store
     * @param id - id of the machine that's to be deleted
     */
    async deleteMachine() {
      const { id } = this.tobeDeletedMachine;
      this.$store.dispatch('machineStore/remove', { id });

      this.removeMachineDialog = false;
      this.tobeDeletedMachine = {};
    },
    /** */
    setMachineSaved(machine) {
      this.$store.dispatch('machineStore/add', {
        machine,
      });
    },
    /**
     * Delete all saved machines, if the machine is currently available, return it to the discovered machines list
     */
    deleteAllSaved() {
      this.$store.getters['machineStore/machines']
        .filter((machine) => machine.saved)
        .map((machine) => machine.id)
        .forEach((id) => {
          this.tobeDeletedMachine = { id };
          this.deleteMachine();
        });
      this.removeMachineDialog = false;
      this.tobeDeletedMachine = {};
    },
    /** */
    connectedMachines(machines) {
      if (machines) {
        return machines.filter((machine) => machine.status === 'CONNECTED');
      }

      return [];
    },
    /** */
    toggleDisplayDetailed(machine) {
      if (this.displayDetailed === machine.id) {
        this.displayDetailed = null;
      } else {
        this.displayDetailed = machine.id;
      }
    },
    /** */
    elementInDisplayDetailed(machine) {
      return machine.id === this.displayDetailed;
    },
  },
  mounted() {
    engineNetworkInterface.startMachinePolling();
  },
  async beforeRouteLeave(to, from, next) {
    await engineNetworkInterface.stopMachinePolling();
    next();
  },
};
</script>
<style lang="scss">
/* https://sass-lang.com/documentation/syntax#scss */

.detailed {
  background-color: #f0f0f0;
  border-style: solid;
  border-color: lightgrey;
  border-left-width: 2px;
  border-right-width: 2px;
}

.detailedBottom {
  border-top-width: 0px;
  border-bottom-width: 2px;
}
</style>