Update single file firmware card layout

Updates will enable a more dynamic firmware page layout
by inspecting all firmware images for host images.

GETs all firmware inventory at Redfish endpoint
'/redfish/v1/UpdateService/FirmwareInventory'
then checks if any image includes RelatedItem
'/redfish/v1/Systems/system/Bios' to determine whether
the UI should show combined or separate firmware cards
for BMC and Host.

This is part of an effort to make the firmware page more dynamic.
These changes are only visisble with ibm dotenv variables.

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I8542a27c6ff421bcb24c8b2570dbe150d5c1ce6c
diff --git a/src/env/components/FirmwareSingleImage/FirmwareFormUpdate.vue b/src/env/components/FirmwareSingleImage/FirmwareFormUpdate.vue
new file mode 100644
index 0000000..f13b8e0
--- /dev/null
+++ b/src/env/components/FirmwareSingleImage/FirmwareFormUpdate.vue
@@ -0,0 +1,235 @@
+<template>
+  <div>
+    <div class="form-background p-3">
+      <b-form @submit.prevent="onSubmitUpload">
+        <b-form-group
+          v-if="isTftpUploadAvailable"
+          :label="
+            $t('pageFirmware.singleFileUpload.form.updateFirmware.fileSource')
+          "
+          :disabled="isPageDisabled"
+        >
+          <b-form-radio v-model="isWorkstationSelected" :value="true">
+            {{
+              $t(
+                'pageFirmware.singleFileUpload.form.updateFirmware.workstation'
+              )
+            }}
+          </b-form-radio>
+          <b-form-radio v-model="isWorkstationSelected" :value="false">
+            {{
+              $t('pageFirmware.singleFileUpload.form.updateFirmware.tftpServer')
+            }}
+          </b-form-radio>
+        </b-form-group>
+
+        <!-- Workstation Upload -->
+        <template v-if="isWorkstationSelected">
+          <b-form-group
+            :label="
+              $t('pageFirmware.singleFileUpload.form.updateFirmware.imageFile')
+            "
+            label-for="image-file"
+          >
+            <b-form-text id="image-file-help-block">
+              {{
+                $t(
+                  'pageFirmware.singleFileUpload.form.updateFirmware.imageFileHelperText'
+                )
+              }}
+            </b-form-text>
+            <form-file
+              id="image-file"
+              accept=".tar"
+              :disabled="isPageDisabled"
+              :state="getValidationState($v.file)"
+              aria-describedby="image-file-help-block"
+              @input="onFileUpload($event)"
+            >
+              <template #invalid>
+                <b-form-invalid-feedback role="alert">
+                  {{ $t('global.form.required') }}
+                </b-form-invalid-feedback>
+              </template>
+            </form-file>
+          </b-form-group>
+        </template>
+
+        <!-- TFTP Server Upload -->
+        <template v-else>
+          <b-form-group
+            :label="
+              $t(
+                'pageFirmware.singleFileUpload.form.updateFirmware.fileAddress'
+              )
+            "
+            label-for="tftp-address"
+          >
+            <b-form-input
+              id="tftp-address"
+              v-model="tftpFileAddress"
+              type="text"
+              :state="getValidationState($v.tftpFileAddress)"
+              :disabled="isPageDisabled"
+              @input="$v.tftpFileAddress.$touch()"
+            />
+            <b-form-invalid-feedback role="alert">
+              {{ $t('global.form.fieldRequired') }}
+            </b-form-invalid-feedback>
+          </b-form-group>
+        </template>
+        <b-btn
+          data-test-id="firmware-button-startUpdate"
+          type="submit"
+          variant="primary"
+          :disabled="isPageDisabled"
+        >
+          {{
+            $t('pageFirmware.singleFileUpload.form.updateFirmware.startUpdate')
+          }}
+        </b-btn>
+        <alert
+          v-if="isServerPowerOffRequired && !isHostOff"
+          variant="warning"
+          :small="true"
+          class="mt-4"
+        >
+          <p class="col-form-label">
+            {{
+              $t(
+                'pageFirmware.singleFileUpload.alert.serverMustBePoweredOffToUpdateFirmware'
+              )
+            }}
+          </p>
+        </alert>
+      </b-form>
+    </div>
+
+    <!-- Modals -->
+    <modal-update-firmware @ok="updateFirmware" />
+  </div>
+</template>
+
+<script>
+import { requiredIf } from 'vuelidate/lib/validators';
+
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+
+import Alert from '@/components/Global/Alert';
+import FormFile from '@/components/Global/FormFile';
+import ModalUpdateFirmware from './FirmwareModalUpdateFirmware';
+
+export default {
+  components: { Alert, FormFile, ModalUpdateFirmware },
+  mixins: [BVToastMixin, LoadingBarMixin, VuelidateMixin],
+  props: {
+    isPageDisabled: {
+      required: true,
+      type: Boolean,
+      default: false,
+    },
+    isHostOff: {
+      required: true,
+      type: Boolean,
+    },
+  },
+  data() {
+    return {
+      loading,
+      isWorkstationSelected: true,
+      file: null,
+      tftpFileAddress: null,
+      isServerPowerOffRequired:
+        process.env.VUE_APP_SERVER_OFF_REQUIRED === 'true',
+    };
+  },
+  computed: {
+    isTftpUploadAvailable() {
+      return this.$store.getters['firmwareSingleImage/isTftpUploadAvailable'];
+    },
+  },
+  watch: {
+    isWorkstationSelected: function () {
+      this.$v.$reset();
+      this.file = null;
+      this.tftpFileAddress = null;
+    },
+  },
+  validations() {
+    return {
+      file: {
+        required: requiredIf(function () {
+          return this.isWorkstationSelected;
+        }),
+      },
+      tftpFileAddress: {
+        required: requiredIf(function () {
+          return !this.isWorkstationSelected;
+        }),
+      },
+    };
+  },
+  created() {
+    this.$store.dispatch('firmwareSingleImage/getUpdateServiceSettings');
+  },
+  methods: {
+    updateFirmware() {
+      this.startLoader();
+      const timerId = setTimeout(() => {
+        this.endLoader();
+        this.infoToast(
+          this.$t('pageFirmware.singleFileUpload.toast.verifyUpdateMessage'),
+          {
+            title: this.$t('pageFirmware.singleFileUpload.toast.verifyUpdate'),
+            refreshAction: true,
+          }
+        );
+      }, 360000);
+      this.infoToast(
+        this.$t('pageFirmware.singleFileUpload.toast.updateStartedMessage'),
+        {
+          title: this.$t('pageFirmware.singleFileUpload.toast.updateStarted'),
+          timestamp: true,
+        }
+      );
+      if (this.isWorkstationSelected) {
+        this.dispatchWorkstationUpload(timerId);
+      } else {
+        this.dispatchTftpUpload(timerId);
+      }
+    },
+    dispatchWorkstationUpload(timerId) {
+      this.$store
+        .dispatch('firmwareSingleImage/uploadFirmware', this.file)
+        .catch(({ message }) => {
+          this.endLoader();
+          this.errorToast(message);
+          clearTimeout(timerId);
+        });
+    },
+    dispatchTftpUpload(timerId) {
+      this.$store
+        .dispatch(
+          'firmwareSingleImage/uploadFirmwareTFTP',
+          this.tftpFileAddress
+        )
+        .catch(({ message }) => {
+          this.endLoader();
+          this.errorToast(message);
+          clearTimeout(timerId);
+        });
+    },
+    onSubmitUpload() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+      this.$bvModal.show('modal-update-firmware');
+    },
+    onFileUpload(file) {
+      this.file = file;
+      this.$v.file.$touch();
+    },
+  },
+};
+</script>