From 64626ebac7529a4cc471781ed45f0782d1b0c79f Mon Sep 17 00:00:00 2001
From: roly <Rolyli.Li@luxshare-ict.com>
Date: Thu, 9 Jan 2025 19:49:05 +0800
Subject: [PATCH] Support luxshare oem firmware update

---
 src/components/Global/LinkButton.vue          |  57 ++
 src/components/Global/UploadFile.vue          | 146 ++++
 src/locales/en-US.json                        |  84 +-
 src/store/modules/Operations/FirmwareStore.js | 220 ++++-
 src/utilities/bus.js                          |   2 +
 src/views/Operations/Firmware/Firmware.vue    | 104 ++-
 .../Operations/Firmware/FirmwareCardsBmc.vue  |  27 +
 .../Operations/Firmware/FirmwareCardsFPGA.vue |  40 +
 .../Firmware/FirmwareCardsFcbCPLD.vue         |  40 +
 .../Operations/Firmware/FirmwareCardsMe.vue   |  40 +
 .../Firmware/FirmwareModalUpdateFirmware.vue  |  29 +
 .../Firmware/FirmwareUpdateStatus.vue         | 822 ++++++++++++++++++
 12 files changed, 1590 insertions(+), 21 deletions(-)
 create mode 100755 src/components/Global/LinkButton.vue
 create mode 100755 src/components/Global/UploadFile.vue
 create mode 100755 src/utilities/bus.js
 create mode 100755 src/views/Operations/Firmware/FirmwareCardsFPGA.vue
 create mode 100755 src/views/Operations/Firmware/FirmwareCardsFcbCPLD.vue
 create mode 100755 src/views/Operations/Firmware/FirmwareCardsMe.vue
 create mode 100755 src/views/Operations/Firmware/FirmwareUpdateStatus.vue

diff --git a/src/components/Global/LinkButton.vue b/src/components/Global/LinkButton.vue
new file mode 100755
index 0000000..2a2022b
--- /dev/null
+++ b/src/components/Global/LinkButton.vue
@@ -0,0 +1,57 @@
+<template>
+  <span
+    :class="{
+      'action-btn': true,
+      'action-btn-primary': !danger,
+      'action-disabled': disabled,
+      'action-btn-danger': danger,
+    }"
+    @click="handleClick()"
+  >
+    <slot></slot>
+  </span>
+</template>
+
+<script>
+export default {
+  props: {
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
+    danger: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  methods: {
+    handleClick() {
+      if (this.disabled) return;
+      else this.$emit('click');
+    },
+  },
+};
+</script>
+
+<style lang="css" scoped>
+.action-btn {
+  white-space: nowrap;
+}
+.action-disabled {
+  cursor: not-allowed !important;
+  color: #ccc !important;
+}
+.action-btn {
+  cursor: pointer;
+}
+.action-btn:hover {
+  opacity: 0.7;
+}
+.action-btn-primary {
+  color: var(--primary);
+}
+.action-btn-danger {
+  margin: 0 5px;
+  color: var(--danger);
+}
+</style>
diff --git a/src/components/Global/UploadFile.vue b/src/components/Global/UploadFile.vue
new file mode 100755
index 0000000..3e492bd
--- /dev/null
+++ b/src/components/Global/UploadFile.vue
@@ -0,0 +1,146 @@
+<template>
+  <b-form
+    class="d-flex align-items-center oem-upload-file"
+    style="max-width: 700px"
+    @submit.prevent="onSubmitUpload"
+  >
+    <b-form-file
+      v-model="file"
+      :class="disabled ? 'upload-file-disabled' : undefined"
+      :disabled="disabled"
+      :placeholder="$t('global.fileUpload.browseText')"
+    ></b-form-file>
+    <div class="d-flex align-items-center ml-2">
+      <b-button
+        :disabled="disabled"
+        class="btn-file"
+        variant="primary"
+        size="sm"
+        type="submit"
+        >{{ $t('pageFirmware.form.updateFirmware.startUpload') }}</b-button
+      >
+      <b-button
+        v-show="!!file"
+        :disabled="disabled"
+        class="btn-file"
+        variant="danger"
+        size="sm"
+        @click="file = null"
+        ><icon-trashcan class="delete-icon"
+      /></b-button>
+    </div>
+    <div class="file-error-tips">
+      {{ fileErrorTips }}
+    </div>
+  </b-form>
+</template>
+
+<script>
+import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+export default {
+  components: {
+    IconTrashcan,
+  },
+  props: {
+    value: {
+      type: File,
+      default: () => {},
+    },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
+    required: {
+      type: Boolean,
+      default: false,
+    },
+    validtedFile: {
+      type: Function,
+      default: () => {},
+    },
+  },
+  data() {
+    return {
+      file: null,
+      fileErrorTips: '',
+    };
+  },
+  watch: {
+    value(newVal) {
+      this.file = newVal;
+    },
+    file: {
+      handler(newVal) {
+        this.$emit('input', newVal);
+        if (!newVal) {
+          this.fileErrorTips = '';
+        } else {
+          this.validtedFile(newVal)
+            .then(() => {
+              this.fileErrorTips = '';
+            })
+            .catch((e) => {
+              this.fileErrorTips = e;
+            });
+        }
+      },
+    },
+  },
+  methods: {
+    onSubmitUpload() {
+      if (!this.file && this.required) {
+        this.fileErrorTips = this.$t('global.form.required');
+        return;
+      }
+      this.validtedFile(this.file)
+        .then(() => {
+          this.$emit('submit', this.file);
+        })
+        .catch((e) => {
+          this.fileErrorTips = e;
+        });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+$btn-height: 33px;
+.btn-file {
+  height: $btn-height;
+  white-space: nowrap;
+}
+.file-error-tips {
+  position: absolute;
+  top: 40px;
+  font-size: 0.875rem;
+  line-height: 1.25rem;
+  color: #da1416;
+}
+</style>
+<style lang="css" scoped>
+.oem-upload-file /deep/ .custom-file-label::after {
+  content: '. . .';
+  cursor: pointer;
+  color: #fff;
+  background: var(--primary);
+}
+.oem-upload-file /deep/ .custom-file-label {
+  margin-bottom: 0;
+}
+.oem-upload-file /deep/ .custom-file {
+  height: 35px !important;
+}
+.oem-upload-file /deep/ .custom-file-input {
+  height: 35px !important;
+  width: 400px;
+}
+.oem-upload-file /deep/ .custom-file-label {
+  height: 35px !important;
+}
+.upload-file-disabled /deep/ .custom-file-label::after {
+  cursor: not-allowed;
+  color: #999;
+  background: #ccc;
+}
+</style>
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 93ad806..0a67945 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -331,6 +331,12 @@
         }
     },
     "pageFirmware": {
+        "primaryImage": "Primary",
+        "secondaryImage": "Secondary",
+        "both": "Both (Not recommended)",
+        "sectionTitleMeCards": "ME Firmware",
+        "sectionTitleFcbCPLDCards": "Fan Board CPLD",
+        "sectionTitleFPGACards": "FPGA",
         "cardActionSwitchToRunning": "Switch to running",
         "cardBodyVersion": "Version",
         "cardBodyReleaseDate": "Release Date",
@@ -340,19 +346,41 @@
         "cardTitleFBCpld": "Fan Board CPLD",
         "sectionTitleBmcCards": "BMC",
         "sectionTitleBmcCardsCombined": "BMC and server",
-        "sectionTitleHostCards": "Host",
+        "sectionTitleHostCards": "BIOS",
         "sectionTitleUpdateFirmware": "Update firmware",
         "sectionTitleCPLDCards": "CPLD",
+        "sectionTitleUpdateFirmwareStatus": "Update firmware status",
         "alert": {
             "operationInProgress": "Server power operation in progress.",
             "serverMustBePoweredOffTo": "Server must be powered off to:",
             "serverMustBePoweredOffToUpdateFirmware": "Server must be powered off to update firmware",
             "switchRunningAndBackupImages": "Switch running and backup images",
             "updateFirmware": "Update firmware",
-            "viewServerPowerOperations": "View server power operations"
+            "viewServerPowerOperations": "View server power operations",
+            "boxupdateFirmware": "Box Update firmware",
+            "updateTask": "Background Update Task"
         },
         "form": {
+            "invalidFiletype": "Invalid file type",
+            "updateOptions": {
+                "keep": "Keep all configurations update",
+                "clear": "Non keep configuration update",
+                "partialKeep": "Keep partial configuration update",
+                "factoryReset": "Factory reset"
+            },
             "updateFirmware": {
+                "updateTimeout": "Update timeout",
+                "UploadedFirmware": "Uploaded %{type}",
+                "updateBmcRestFail": "BMC reset failed",
+                "updateSecondaryInfo": "Secondary BMC is now updating, please wait",
+                "updateFinished": "%{type} %{version} Updated",
+                "ok": "Ok",
+                "version": "Version",
+                "biosKeepConfig": "Keep Configuration",
+                "biosNonKeepConfig": "Non Keep Configuration",
+                "options": "Options",
+                "startUpload": "Upload image",
+                "updateAll": "Update all",
                 "fileAddress": "File address",
                 "fileSource": "File source",
                 "imageFile": "Image file",
@@ -362,6 +390,11 @@
             }
         },
         "modal": {
+            "updateAllConfirm": "Are you sure you want to update all?",
+            "deleteConfirm": "Are you sure you want to delete the %{firmware} firmware?",
+            "updateFirmwareInfoOemBmc": "The primary(secondary) BMC firmware will be updated as you choose. In some scenarios, the BMC will reboot. Please confirm whether to update BMC firmware immediately?",
+            "updateSuccessOem": "update task is queued and will be activated when host DC power cycle by BMC (IPMI command or Redfish I/F)",
+            "updateFirmwareInfoOem": "The new image will be async activated. After that, the host will reboot automatically to run from the new image.",
             "switchImages": "Switch images",
             "switchRunningImage": "Switch running image",
             "switchRunningImageInfo": "A BMC reboot is required to run the backup image. The application might be unresponsive during this time.",
@@ -372,16 +405,61 @@
             "updateFirmwareInfoDefault": "The new image will be uploaded and activated. After that, the BMC or host will reboot automatically to run from the new image."
         },
         "toast": {
+            "errorUpdateAsyncAndSync": "BMC and other firmware cannot be updated simultaneously.",
+            "errorUpdateInPost": "The unit is in POST stage, can not flash BMC. Please wait a moment and try again.",
+            "errorUpdateInPeerNodeUpdating": "The peer node is upgrading components. Shared components do not allow dual nodes to be upgraded at the same time. Please try again later.",
+            "bmcUpdateTips": "BMC updates can cause other firmware to be lost.",
+            "errorUpdateFirmwareSign": "The firmware update failed or the signature was invalid.",
+            "errorInPostStatus": "BMC firmware can not update in BIOS POST status",
+            "uploadStarted": "Upload started",
+            "uploadStartedMessage": "Wait for the firmware upload notification before making any changes.",
+            "timeoutUpload": "Update timeout",
+            "timeoutUploadMessage": "Upload firmware image timeout",
+            "uploadSuccess": "Upload success",
+            "uploadSuccessMessage": "The firmware upload successfully",
+            "uploadFailed": "Upload failed",
+            "uploadFailedMessage": "The firmware upload failed",
+            "errorVerifyFirmware": "Failed to verify the signature image",
+            "errorUploadFirmware": "Failed to extract or verify the image",
             "errorSwitchImages": "Error switching running and backup images.",
             "errorUpdateFirmware": "Error starting firmware update.",
             "rebootStarted": "Reboot started",
             "rebootStartedMessage": "Successfully started reboot from backup image.",
             "updateStarted": "Update started",
             "updateStartedMessage": "Wait for the firmware update notification before making any changes.",
+            "updateSuccess": "Update success",
+            "updateSuccessMessage": "Successfully update the firmware, Please wait for the image refresh",
+            "updateFailed": "Update failed",
+            "updateFailedMessage": "Failed in update the firmware.",
             "verifySwitch": "Verify switch",
             "verifySwitchMessage": "Refresh the application to verify the running and backup images switched.",
             "verifyUpdate": "Verify update",
-            "verifyUpdateMessage": "Refresh the application to verify firmware updated successfully"
+            "verifyUpdateMessage": "Refresh the application to verify firmware updated successfully",
+            "errorGetUpdateStatus": "Failed to retrieve firmware update status.",
+            "deleteSuccess": "Deletion successful.",
+            "firmwareDeleted": "Firmware %{firmware} has been deleted.",
+            "deleteFailed": "Failed to delete firmware %{firmware}.",
+            "bmcUpdatingTips": "The page functionality is temporarily unavailable during the BMC firmware update.",
+            "bmcUpdateCompleteTips": "BMC upgrade completed. Please refresh the page after a moment. Note that the IP address may change in some cases.",
+            "validateFirmwareFailed": "Failed to validate the %{firmware} firmware image.",
+            "firmwareIdMismatch": "The %{firmware} firmware does not match"
+        },
+        "table": {
+            "updateComplete": "Update completed",
+            "updateFailed": "Update failed",
+            "updating": "Updating",
+            "waitUpdate": "Wait for update",
+            "emptyMessage": "No firmware available",
+            "update": "update",
+            "delete": "delete",
+            "noupdate": "No update",
+            "name": "Unit name",
+            "status": "Update Status",
+            "time": "Estimated time",
+            "progress": "Progress",
+            "firmwareName": "Name",
+            "firmwareStatus": "Status",
+            "firmwareVersion": "Version"
         }
     },
     "pageInventory": {
diff --git a/src/store/modules/Operations/FirmwareStore.js b/src/store/modules/Operations/FirmwareStore.js
index 001eaf6..39b71e8 100644
--- a/src/store/modules/Operations/FirmwareStore.js
+++ b/src/store/modules/Operations/FirmwareStore.js
@@ -1,20 +1,57 @@
 import api from '@/store/api';
 import i18n from '@/i18n';
+import { bus } from '@/utilities/bus';
 
+const FIRMWARE_PURPOSE = {
+  BMC: 'xyz.openbmc_project.Software.Version.VersionPurpose.BMC',
+  CPLD: 'xyz.openbmc_project.Software.Version.VersionPurpose.CPLD',
+  FPGA: 'xyz.openbmc_project.Software.Version.VersionPurpose.FPGA',
+  BIOS: 'xyz.openbmc_project.Software.Version.VersionPurpose.Host',
+  PSU: 'xyz.openbmc_project.Software.Version.VersionPurpose.PSU',
+  HSBP: 'xyz.openbmc_project.Software.Version.VersionPurpose.HSBP',
+  FCB: 'xyz.openbmc_project.Software.Version.VersionPurpose.FCB',
+};
 const FirmwareStore = {
   namespaced: true,
   state: {
     bmcFirmware: [],
     hostFirmware: [],
+    uploadFirmwareType: null,
+    uploadFirmwareState: null,
+    uploadFirmwareId: null,
+    firmwareFunctionData: [],
+    meFirmware: [],
     cpldFirmware: [],
-    fbcpldFirmware: [],
+    fcbCpldFirmware: [],
+    fpgaFirmware: [],
     bmcActiveFirmwareId: null,
     hostActiveFirmwareId: null,
     applyTime: null,
     httpPushUri: null,
     tftpAvailable: false,
+    firmwareStatus: null,
+    isFirmwareProgress: false,
+    isNeedCycle: false,
+    bmcIsUpdating: false,
+    bmcIsUpdatingfCurrentPartition: false,
   },
   getters: {
+    uploadFirmwareId: (state) => state.uploadFirmwareId,
+    uploadFirmwareState: (state) => state.uploadFirmwareState,
+    uploadFirmwareType: (state) => state.uploadFirmwareType,
+    priorityFirmware: (state) => {
+      return state.firmwareFunctionData.find(
+        (firmware) => firmware.Purpose === FIRMWARE_PURPOSE.BMC
+      );
+    },
+    meFirmware: (state) => {
+      return state.meFirmware.find((firmware) => firmware.id === 'me');
+    },
+    fpgaFirmware: (state) => {
+      return state.fpgaFirmware.find(
+        (firmware) => firmware.id === 'fpga_active'
+      );
+    },
     isTftpUploadAvailable: (state) => state.tftpAvailable,
     isSingleFileUploadEnabled: (state) => state.hostFirmware.length === 0,
     activeBmcFirmware: (state) => {
@@ -29,7 +66,9 @@ const FirmwareStore = {
     },
     backupBmcFirmware: (state) => {
       return state.bmcFirmware.find(
-        (firmware) => firmware.id !== state.bmcActiveFirmwareId
+        (firmware) =>
+          firmware.id !== state.bmcActiveFirmwareId &&
+          firmware.state === 'Enabled'
       );
     },
     backupHostFirmware: (state) => {
@@ -42,28 +81,53 @@ const FirmwareStore = {
         (firmware) => firmware.id === 'cpld_active'
       );
     },
-    fbcpldFirmware: (state) => {
-      return state.fbcpldFirmware.find(
-        (firmware) => firmware.id === 'fb_cpld_active'
+    fcbCpldFirmware: (state) => {
+      return state.fcbCpldFirmware.find(
+        (firmware) => firmware.id === 'fcb_cpld_active'
       );
     },
+    allFirmwareStatus: (state) => state.firmwareStatus,
+    isFirmwareProgress: (state) => state.isFirmwareProgress,
+    isNeedCycle: (state) => state.isNeedCycle,
+    bmcIsUpdating: (state) => state.bmcIsUpdating,
+    bmcIsUpdatingfCurrentPartition: (state) =>
+      state.bmcIsUpdatingfCurrentPartition,
   },
   mutations: {
+    setFirmwareFunctionData: (state, firmware) =>
+      (state.firmwareFunctionData = firmware),
+    setMeFirmware: (state, firmware) => (state.meFirmware = firmware),
+    setFPGAFirmware: (state, firmware) => (state.fpgaFirmware = firmware),
     setActiveBmcFirmwareId: (state, id) => (state.bmcActiveFirmwareId = id),
     setActiveHostFirmwareId: (state, id) => (state.hostActiveFirmwareId = id),
     setBmcFirmware: (state, firmware) => (state.bmcFirmware = firmware),
     setHostFirmware: (state, firmware) => (state.hostFirmware = firmware),
     setCPLDFirmware: (state, firmware) => (state.cpldFirmware = firmware),
-    setFBCPLDFirmware: (state, firmware) => (state.fbcpldFirmware = firmware),
+    setFcbCpldFirmware: (state, firmware) => (state.fcbCpldFirmware = firmware),
     setApplyTime: (state, applyTime) => (state.applyTime = applyTime),
     setHttpPushUri: (state, httpPushUri) => (state.httpPushUri = httpPushUri),
     setTftpUploadAvailable: (state, tftpAvailable) =>
       (state.tftpAvailable = tftpAvailable),
+    setFirmwareUpdateStatus: (state, status) => (state.firmwareStatus = status),
+    setIsFirmwareProgress: (state, isProgress) =>
+      (state.isFirmwareProgress = isProgress),
+    setIsNeedCycle: (state, isNeedCycle) => (state.isNeedCycle = isNeedCycle),
+    setBmcIsUpdating: (state, bmcIsUpdating) =>
+      (state.bmcIsUpdating = bmcIsUpdating),
+    setBmcIsUpdatingfCurrentPartition: (state, bool) => {
+      state.bmcIsUpdatingfCurrentPartition = bool;
+    },
+    setBmcUpdateProgress: (state, progress) => {
+      const bmcFirmware = state.firmwareStatus.find((it) => it.BMC);
+      bmcFirmware.BMC.UpdateProgress = progress;
+      bmcFirmware.BMC.UpdateStatus = progress === 100 ? 'Updated' : 'Updating';
+    },
   },
   actions: {
     async getFirmwareInformation({ dispatch }) {
       dispatch('getActiveHostFirmware');
       dispatch('getActiveBmcFirmware');
+      dispatch('getFirmwareFunctionData');
       return await dispatch('getFirmwareInventory');
     },
     getActiveBmcFirmware({ commit }) {
@@ -94,10 +158,12 @@ const FirmwareStore = {
       await api
         .all(inventoryList)
         .then((response) => {
+          const meFirmware = [];
+          const fpgaFirmware = [];
           const bmcFirmware = [];
           const hostFirmware = [];
           const cpldFirmware = [];
-          const fbcpldFirmware = [];
+          const fcbCpldFirmware = [];
           response.forEach(({ data }) => {
             const firmwareType = data?.RelatedItem?.[0]?.['@odata.id']
               .split('/')
@@ -108,24 +174,29 @@ const FirmwareStore = {
               location: data?.['@odata.id'],
               status: data?.Status?.Health,
               reldate: data?.ReleaseDate,
+              state: data?.Status?.State,
             };
             if (firmwareType === 'bmc') {
               bmcFirmware.push(item);
             } else if (firmwareType === 'Bios') {
               hostFirmware.push(item);
             }
-
             if (item.id === 'cpld_active') {
               cpldFirmware.push(item);
-            }
-            if (item.id === 'fb_cpld_active') {
-              fbcpldFirmware.push(item);
+            } else if (item.id === 'fpga_active') {
+              fpgaFirmware.push(item);
+            } else if (item.id === 'me') {
+              meFirmware.push(item);
+            } else if (item.id === 'fcb_cpld_active') {
+              fcbCpldFirmware.push(item);
             }
           });
           commit('setBmcFirmware', bmcFirmware);
           commit('setHostFirmware', hostFirmware);
           commit('setCPLDFirmware', cpldFirmware);
-          commit('setFBCPLDFirmware', fbcpldFirmware);
+          commit('setFcbCpldFirmware', fcbCpldFirmware);
+          commit('setFPGAFirmware', fpgaFirmware);
+          commit('setMeFirmware', meFirmware);
         })
         .catch((error) => {
           console.log(error);
@@ -214,6 +285,131 @@ const FirmwareStore = {
           throw new Error(i18n.t('pageFirmware.toast.errorSwitchImages'));
         });
     },
+    // ======================== firmware OEM ========================
+    getFirmwareFunctionData({ commit }) {
+      return api
+        .get('/xyz/openbmc_project/software/functional')
+        .then((response) =>
+          api.all(response.data.data.endpoints.map((bmcurl) => api.get(bmcurl)))
+        )
+        .then((bmcResult) => {
+          const bmcStateData = bmcResult.map((datas) => datas.data.data);
+          commit('setFirmwareFunctionData', bmcStateData);
+        })
+        .catch((error) => {
+          console.log(error);
+        });
+    },
+    // ----------------- firmware update ----------
+    async uploadFirmwareOEM({ commit }, image) {
+      console.log('image', image);
+      const data = new FormData();
+      data.append('fwimage', image);
+      commit('setIsFirmwareProgress', true);
+      return await api
+        .post(
+          '/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareFile',
+          data
+        )
+        .then((response) => {
+          console.log('upload success', response);
+          return response;
+        })
+        .catch((error) => {
+          console.log('upload error', error);
+          throw new Error(i18n.t('pageFirmware.toast.errorUploadFirmware'));
+        })
+        .finally(() => commit('setIsFirmwareProgress', false));
+    },
+    async getAllUpdateStatus({ commit }) {
+      return await api
+        .get('/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareStatus')
+        .then((response) => {
+          // if (state.bmcIsUpdating) return;
+          commit('setFirmwareUpdateStatus', response.data.Oem.Luxshare);
+          const isNeedCycle = response.data.Oem.Luxshare.find((firmware) => {
+            const entries = Object.entries(firmware);
+            return (
+              entries[0][1].UpdateStatus === 'Queued' ||
+              (entries[0][0] === 'Retimer' &&
+                entries[0][1].UpdateStatus === 'Updated')
+            );
+          });
+          commit('setIsNeedCycle', !!isNeedCycle);
+        })
+        .catch((error) => {
+          console.log(error);
+          throw new Error(i18n.t('pageFirmware.toast.errorGetUpdateStatus'));
+        });
+    },
+    async startUpdate({ commit }, updateConfigs) {
+      commit('setIsFirmwareProgress', true);
+      return await api
+        .post(
+          '/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareUpdate',
+          updateConfigs
+        )
+        .finally(() => commit('setIsFirmwareProgress', false));
+    },
+    async deleteFirmware(_, target) {
+      console.log('target', target);
+      return await api.delete(
+        '/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareUpdate',
+        {
+          data: {
+            Target: target,
+          },
+        }
+      );
+    },
+    async pollingBmcUpdateStatus() {
+      const timeout = 3000;
+      return new Promise((resolve, reject) => {
+        setTimeout(() => {
+          reject();
+        }, timeout);
+        api
+          .get('/redfish/v1/UpdateService/Actions/Oem/Luxshare/FirmwareStatus')
+          .then((res) => {
+            const progress = res.data.Oem.Luxshare?.[0]?.BMC?.UpdateProgress;
+            resolve(progress);
+          })
+          .catch((error) => {
+            reject(error);
+          });
+      });
+    },
+    getBmcUpdateStatus({ state, commit, dispatch }) {
+      dispatch('pollingBmcUpdateStatus')
+        .then((progress) => {
+          const bmcFirmware = state.firmwareStatus.find((it) => it.BMC);
+          if (bmcFirmware.BMC.UpdateProgress === 100) {
+            return;
+          } else if (
+            progress !== undefined &&
+            progress > bmcFirmware.BMC.UpdateProgress
+          ) {
+            commit('setBmcUpdateProgress', progress);
+          }
+          if (progress === 100) {
+            bus.$emit('bmcUpdateComplete');
+            localStorage.setItem('loggingStatus', 'logout');
+            commit('authentication/logout', null, { root: true });
+          } else {
+            setTimeout(() => {
+              dispatch('getBmcUpdateStatus');
+            }, 3000);
+          }
+        })
+        .catch(() => {
+          const bmcFirmware = state.firmwareStatus.find((it) => it.BMC);
+          if (bmcFirmware.BMC.UpdateProgress === 100) return;
+          setTimeout(() => {
+            dispatch('getBmcUpdateStatus');
+          }, 3000);
+        });
+    },
+    // ======================== END ========================
   },
 };
 
diff --git a/src/utilities/bus.js b/src/utilities/bus.js
new file mode 100755
index 0000000..68259ae
--- /dev/null
+++ b/src/utilities/bus.js
@@ -0,0 +1,2 @@
+import Vue from 'vue';
+export const bus = new Vue();
diff --git a/src/views/Operations/Firmware/Firmware.vue b/src/views/Operations/Firmware/Firmware.vue
index 25fe0bb..106da18 100644
--- a/src/views/Operations/Firmware/Firmware.vue
+++ b/src/views/Operations/Firmware/Firmware.vue
@@ -13,15 +13,37 @@
         <bmc-cards :is-page-disabled="isPageDisabled" />
 
         <!-- Host Firmware -->
-        <host-cards v-if="!isSingleFileUploadEnabled" />
-
-        <!-- CPLD Firmware -->
-        <cpld-cards v-if="cpld" />
+        <b-row>
+          <b-col xl="6">
+            <!-- Host Firmware -->
+            <host-cards v-if="!isSingleFileUploadEnabled" />
+          </b-col>
+          <b-col xl="6">
+            <!-- Me Firmware -->
+            <me-cards v-if="me" />
+          </b-col>
+        </b-row>
+        <b-row>
+          <b-col xl="6">
+            <!-- CPLD Firmware -->
+            <cpld-cards v-if="cpld" />
+          </b-col>
+          <b-col xl="6">
+            <!--Fan CPLD Firmware -->
+            <fcb-cpld-cards v-if="fcbCpld" />
+          </b-col>
+        </b-row>
+        <b-row>
+          <b-col xl="6">
+            <fpga-cards v-if="fpga" />
+          </b-col>
+        </b-row>
       </b-col>
     </b-row>
 
     <!-- Update firmware-->
     <page-section
+      v-if="!isOEMUpdate"
       :section-title="$t('pageFirmware.sectionTitleUpdateFirmware')"
     >
       <b-row>
@@ -34,17 +56,29 @@
         </b-col>
       </b-row>
     </page-section>
+
+    <!-- Update firmware status-->
+    <page-section
+      v-if="isOEMUpdate"
+      :section-title="$t('pageFirmware.sectionTitleUpdateFirmwareStatus')"
+    >
+      <update-status :is-page-disabled="isPageDisabled"></update-status>
+    </page-section>
   </b-container>
 </template>
 
 <script>
 import AlertsServerPower from './FirmwareAlertServerPower';
+import MeCards from './FirmwareCardsMe';
+import FcbCpldCards from './FirmwareCardsFcbCPLD';
+import FpgaCards from './FirmwareCardsFPGA';
 import BmcCards from './FirmwareCardsBmc';
 import FormUpdate from './FirmwareFormUpdate';
 import HostCards from './FirmwareCardsHost';
 import PageSection from '@/components/Global/PageSection';
 import PageTitle from '@/components/Global/PageTitle';
 import CpldCards from './FirmwareCardsCPLD';
+import UpdateStatus from './FirmwareUpdateStatus';
 
 import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin';
 
@@ -52,12 +86,16 @@ export default {
   name: 'FirmwareSingleImage',
   components: {
     AlertsServerPower,
+    MeCards,
+    FcbCpldCards,
+    FpgaCards,
     BmcCards,
     FormUpdate,
     HostCards,
     PageSection,
     PageTitle,
     CpldCards,
+    UpdateStatus,
   },
   mixins: [LoadingBarMixin],
   beforeRouteLeave(to, from, next) {
@@ -66,12 +104,36 @@ export default {
   },
   data() {
     return {
+      isOEMUpdate: true, //false: original openbmc update
+      isUploaded: false,
       loading,
       isServerPowerOffRequired:
         process.env.VUE_APP_SERVER_OFF_REQUIRED === 'true',
     };
   },
   computed: {
+    // is in upload or update, Used to gray out when switching back and forth with other pages.
+    isFirmwareProgress() {
+      return this.$store.getters['firmware/isFirmwareProgress'];
+    },
+    uploadFirmwareId() {
+      return this.$store.getters['firmware/uploadFirmwareId'];
+    },
+    uploadFirmwareType() {
+      return this.$store.getters['firmware/uploadFirmwareType'];
+    },
+    uploadFirmwareState() {
+      return this.$store.getters['firmware/uploadFirmwareState'];
+    },
+    me() {
+      return this.$store.getters['firmware/meFirmware'];
+    },
+    fcbCpld() {
+      return this.$store.getters['firmware/fcbCpldFirmware'];
+    },
+    fpga() {
+      return this.$store.getters['firmware/fpgaFirmware'];
+    },
     serverStatus() {
       return this.$store.getters['global/serverStatus'];
     },
@@ -81,11 +143,28 @@ export default {
     isSingleFileUploadEnabled() {
       return this.$store.getters['firmware/isSingleFileUploadEnabled'];
     },
+    isOperationInProgress() {
+      return this.$store.getters['controls/isOperationInProgress'];
+    },
+    isReadOnly() {
+      return this.$store.getters['global/userPrivilege'] === 'ReadOnly';
+    },
     isPageDisabled() {
       if (this.isServerPowerOffRequired) {
-        return !this.isServerOff || this.loading || this.isOperationInProgress;
+        return (
+          !this.isServerOff ||
+          this.loading ||
+          this.isOperationInProgress ||
+          this.isFirmwareProgress ||
+          this.isReadOnly
+        );
       }
-      return this.loading || this.isOperationInProgress;
+      return (
+        this.loading ||
+        this.isOperationInProgress ||
+        this.isFirmwareProgress ||
+        this.isReadOnly
+      );
     },
     cpld() {
       return this.$store.getters['firmware/cpldFirmware'];
@@ -93,9 +172,22 @@ export default {
   },
   created() {
     this.startLoader();
+    this.$root.$on('set-upload-event', (evt, isUploaded) => {
+      this.isUploaded = isUploaded;
+      this.loading = evt;
+    });
+    this.$root.$on('set-update-event', (evt) => {
+      this.loading = evt;
+    });
     this.$store
       .dispatch('firmware/getFirmwareInformation')
       .finally(() => this.endLoader());
   },
 };
 </script>
+
+<style lang="scss" scoped>
+.upload-cell {
+  padding-bottom: $spacer * 1;
+}
+</style>
diff --git a/src/views/Operations/Firmware/FirmwareCardsBmc.vue b/src/views/Operations/Firmware/FirmwareCardsBmc.vue
index 8765f77..37c9cf8 100644
--- a/src/views/Operations/Firmware/FirmwareCardsBmc.vue
+++ b/src/views/Operations/Firmware/FirmwareCardsBmc.vue
@@ -10,6 +10,8 @@
             </p>
           </template>
           <dl class="mb-0">
+            <dd>{{ priorityFirmware }}</dd>
+
             <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
             <dd class="mb-0">{{ runningVersion }}</dd>
           </dl>
@@ -28,6 +30,8 @@
             </p>
           </template>
           <dl>
+            <dd>{{ backupFirmware }}</dd>
+
             <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
             <dd>
               <status-icon v-if="showBackupImageStatus" status="danger" />
@@ -82,6 +86,26 @@ export default {
     };
   },
   computed: {
+    priorityFirmware() {
+      const pri = this.$store.getters['firmware/priorityFirmware'];
+      if (pri?.Priority === 0) {
+        return this.$t('pageFirmware.primaryImage');
+      }
+      if (pri?.Priority === 1) {
+        return this.$t('pageFirmware.secondaryImage');
+      }
+      return '';
+    },
+    backupFirmware() {
+      const pri = this.$store.getters['firmware/priorityFirmware'];
+      if (pri?.Priority === 0) {
+        return this.$t('pageFirmware.secondaryImage');
+      }
+      if (pri?.Priority === 1) {
+        return this.$t('pageFirmware.primaryImage');
+      }
+      return '';
+    },
     isSingleFileUploadEnabled() {
       return this.$store.getters['firmware/isSingleFileUploadEnabled'];
     },
@@ -114,6 +138,9 @@ export default {
         this.backupStatus === 'Critical' || this.backupStatus === 'Warning'
       );
     },
+    backupReleaseDate() {
+      return this.backup?.reldate || '--';
+    },
   },
   methods: {
     switchToRunning() {
diff --git a/src/views/Operations/Firmware/FirmwareCardsFPGA.vue b/src/views/Operations/Firmware/FirmwareCardsFPGA.vue
new file mode 100755
index 0000000..f1f5ddd
--- /dev/null
+++ b/src/views/Operations/Firmware/FirmwareCardsFPGA.vue
@@ -0,0 +1,40 @@
+<template>
+  <page-section :section-title="$t('pageFirmware.sectionTitleFPGACards')">
+    <b-card-group deck>
+      <!-- Running image -->
+      <b-card>
+        <template #header>
+          <p class="font-weight-bold m-0">
+            {{ $t('pageFirmware.cardTitleRunning') }}
+          </p>
+        </template>
+        <dl class="mb-0">
+          <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+          <dd class="mb-0">{{ runningVersion }}</dd>
+        </dl>
+      </b-card>
+    </b-card-group>
+  </page-section>
+</template>
+
+<script>
+import PageSection from '@/components/Global/PageSection';
+
+export default {
+  components: { PageSection },
+  computed: {
+    running() {
+      return this.$store.getters['firmware/fpgaFirmware'];
+    },
+    runningVersion() {
+      return this.running?.version || '--';
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.page-section {
+  margin-top: -$spacer * 1.5;
+}
+</style>
diff --git a/src/views/Operations/Firmware/FirmwareCardsFcbCPLD.vue b/src/views/Operations/Firmware/FirmwareCardsFcbCPLD.vue
new file mode 100755
index 0000000..79294d5
--- /dev/null
+++ b/src/views/Operations/Firmware/FirmwareCardsFcbCPLD.vue
@@ -0,0 +1,40 @@
+<template>
+  <page-section :section-title="$t('pageFirmware.sectionTitleFcbCPLDCards')">
+    <b-card-group deck>
+      <!-- Running image -->
+      <b-card>
+        <template #header>
+          <p class="font-weight-bold m-0">
+            {{ $t('pageFirmware.cardTitleRunning') }}
+          </p>
+        </template>
+        <dl class="mb-0">
+          <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+          <dd class="mb-0">{{ runningVersion }}</dd>
+        </dl>
+      </b-card>
+    </b-card-group>
+  </page-section>
+</template>
+
+<script>
+import PageSection from '@/components/Global/PageSection';
+
+export default {
+  components: { PageSection },
+  computed: {
+    running() {
+      return this.$store.getters['firmware/fcbCpldFirmware'];
+    },
+    runningVersion() {
+      return this.running?.version || '--';
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.page-section {
+  margin-top: -$spacer * 1.5;
+}
+</style>
diff --git a/src/views/Operations/Firmware/FirmwareCardsMe.vue b/src/views/Operations/Firmware/FirmwareCardsMe.vue
new file mode 100755
index 0000000..8828463
--- /dev/null
+++ b/src/views/Operations/Firmware/FirmwareCardsMe.vue
@@ -0,0 +1,40 @@
+<template>
+  <page-section :section-title="$t('pageFirmware.sectionTitleMeCards')">
+    <b-card-group deck>
+      <!-- Running image -->
+      <b-card>
+        <template #header>
+          <p class="font-weight-bold m-0">
+            {{ $t('pageFirmware.cardTitleRunning') }}
+          </p>
+        </template>
+        <dl class="mb-0">
+          <dt>{{ $t('pageFirmware.cardBodyVersion') }}</dt>
+          <dd class="mb-0">{{ runningVersion }}</dd>
+        </dl>
+      </b-card>
+    </b-card-group>
+  </page-section>
+</template>
+
+<script>
+import PageSection from '@/components/Global/PageSection';
+
+export default {
+  components: { PageSection },
+  computed: {
+    running() {
+      return this.$store.getters['firmware/meFirmware'];
+    },
+    runningVersion() {
+      return this.running?.version || '--';
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.page-section {
+  margin-top: -$spacer * 1.5;
+}
+</style>
diff --git a/src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue b/src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue
index 1835521..79ee979 100644
--- a/src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue
+++ b/src/views/Operations/Firmware/FirmwareModalUpdateFirmware.vue
@@ -6,6 +6,9 @@
     :cancel-title="$t('global.action.cancel')"
     @ok="$emit('ok')"
   >
+    <p v-if="uploadFirmwareType === 'BMC' && firmwareLength > 1">
+      {{ $t('pageFirmware.toast.bmcUpdateTips') }}
+    </p>
     <template v-if="isSingleFileUploadEnabled">
       <p>
         {{ $t('pageFirmware.modal.updateFirmwareInfo') }}
@@ -21,6 +24,22 @@
         {{ $t('pageFirmware.modal.updateFirmwareInfo3') }}
       </p>
     </template>
+    <template
+      v-else-if="
+        uploadFirmwareType == 'BIOS' ||
+        uploadFirmwareType == 'CPLD' ||
+        uploadFirmwareType == 'FPGA' ||
+        uploadFirmwareType == 'HSBP' ||
+        uploadFirmwareType == 'FCB'
+      "
+    >
+      {{ $t('pageFirmware.modal.updateFirmwareInfoOem') }}
+    </template>
+    <template
+      v-else-if="uploadFirmwareType && uploadFirmwareType.indexOf('BMC') !== -1"
+    >
+      {{ $t('pageFirmware.modal.updateFirmwareInfoOemBmc') }}
+    </template>
     <template v-else>
       {{ $t('pageFirmware.modal.updateFirmwareInfoDefault') }}
     </template>
@@ -29,6 +48,16 @@
 
 <script>
 export default {
+  props: {
+    uploadFirmwareType: {
+      type: String,
+      default: '',
+    },
+    firmwareLength: {
+      type: Number,
+      default: 0,
+    },
+  },
   computed: {
     runningBmc() {
       return this.$store.getters['firmware/activeBmcFirmware'];
diff --git a/src/views/Operations/Firmware/FirmwareUpdateStatus.vue b/src/views/Operations/Firmware/FirmwareUpdateStatus.vue
new file mode 100755
index 0000000..e2fd859
--- /dev/null
+++ b/src/views/Operations/Firmware/FirmwareUpdateStatus.vue
@@ -0,0 +1,822 @@
+<template>
+  <b-overlay
+    z-index="2000"
+    class="firmware-overlay"
+    :show="showOverlay"
+    :opacity="opacity"
+    spinner-type="none"
+  >
+    <b-container
+      class="firmware-update-container"
+      fluid="xl"
+      style="max-width: 1440px"
+    >
+      <b-row>
+        <b-col xl="10" style="margin-bottom: 30px">
+          <div class="d-flex justify-content-between align-items-center">
+            <upload-file
+              v-model="file"
+              required
+              :disabled="isPageDisabled"
+              :validted-file="validtedFileType"
+              @submit="uploadImage"
+            ></upload-file>
+            <b-button
+              v-if="!hasUploadedBmc"
+              :disabled="
+                runningOnSecondaryContainBMC ||
+                isPageDisabled ||
+                !allFirmwareStatus.some((it) => it.UpdateStatus === 'Uploaded')
+              "
+              variant="primary"
+              size="sm"
+              class="btn-file"
+              style="flex-shrink: 0; margin-left: 50px; height: 35px"
+              @click="updateAll"
+              >{{ $t('pageFirmware.form.updateFirmware.updateAll') }}</b-button
+            >
+          </div>
+        </b-col>
+        <b-col xl="10">
+          <b-table
+            show-empty
+            :empty-text="$t('pageFirmware.table.emptyMessage')"
+            :fields="fields"
+            striped
+            hover
+            :items="allFirmwareStatus"
+          >
+            <template #row-details="row">
+              <div v-if="row.item.name === 'BMC'">
+                <b-form-group
+                  :label="$t('pageFirmware.form.updateFirmware.options')"
+                >
+                  <div class="d-flex">
+                    <b-form-radio-group
+                      v-model="bmcConfig"
+                      style="margin-right: 40px"
+                      :disabled="bmcConfigDisabled"
+                      :options="getUpdateOptions('BMC')"
+                      stacked
+                    ></b-form-radio-group>
+                    <b-form-radio-group
+                      v-model="bmcConfig2"
+                      :disabled="isPageDisabled"
+                      :options="bmcConfigOptions"
+                      stacked
+                    ></b-form-radio-group>
+                  </div>
+                </b-form-group>
+              </div>
+              <div v-if="row.item.name === 'BIOS'">
+                <b-form-group
+                  :label="$t('pageFirmware.form.updateFirmware.options')"
+                  :disabled="isPageDisabled"
+                >
+                  <b-form-radio-group
+                    v-model="biosConfig2"
+                    :disabled="isPageDisabled"
+                    :options="biosConfigOptions"
+                    stacked
+                  ></b-form-radio-group>
+                </b-form-group>
+              </div>
+              <div v-if="row.item.name === 'Retimer'">
+                <b-form-group
+                  :label="$t('pageFirmware.form.updateFirmware.options')"
+                  :disabled="isPageDisabled"
+                  style="width: 200px"
+                >
+                  <b-form-select
+                    v-model="retimerConfig"
+                    :options="retimerOptions"
+                  ></b-form-select>
+                </b-form-group>
+              </div>
+            </template>
+            <template #cell(status)="{ item }">
+              <div
+                v-if="item.UpdateStatus === 'Updating'"
+                class="d-flex align-items-center"
+              >
+                <div style="width: 140px">
+                  <b-progress style="border-radius: 20px" :max="100">
+                    <b-progress-bar
+                      :value="item.UpdateProgress"
+                    ></b-progress-bar>
+                  </b-progress>
+                </div>
+                <div>
+                  {{ updatingBmcPartitionName(item) }}
+                  {{ item.UpdateStatus }}-{{ item.UpdateProgress }}%
+                </div>
+              </div>
+              <div
+                v-else-if="item.UpdateStatus === 'Updated'"
+                class="d-flex align-items-center"
+              >
+                {{ updatingBmcPartitionName(item) }}
+                {{
+                  updatingBmcPartitionName(item) !== 'Updating'
+                    ? ' Updated'
+                    : ''
+                }}
+              </div>
+              <div v-else class="d-flex align-items-center">
+                <div
+                  v-if="item.UpdateStatus === 'Conflicted'"
+                  class="mr-1"
+                  :title="
+                    $t('pageFirmware.toast.errorUpdateInPeerNodeUpdating')
+                  "
+                >
+                  <status-icon status="danger" />
+                </div>
+                {{ item.UpdateStatus }}
+              </div>
+            </template>
+            <template #cell(version)="{ item }">{{
+              item.VersionStatus
+            }}</template>
+            <template #cell(Action)="{ item }">
+              <link-button
+                :disabled="isPageDisabled || item.UpdateStatus !== 'Uploaded'"
+                @click="handleUpdate(item)"
+                >{{ $t('pageFirmware.table.update') }}</link-button
+              >
+              <link-button
+                :disabled="
+                  isPageDisabled ||
+                  ['Updated', 'Updating'].includes(item.UpdateStatus)
+                "
+                danger
+                @click="handleDelete(item)"
+                >{{ $t('pageFirmware.table.delete') }}</link-button
+              >
+            </template>
+          </b-table></b-col
+        ></b-row
+      >
+      <modal-update-firmware
+        :upload-firmware-type="updatingFirmware && updatingFirmware.name"
+        :firmware-length="allFirmwareStatus && allFirmwareStatus.length"
+        @ok="handleUpdateFirmware"
+      />
+    </b-container>
+  </b-overlay>
+</template>
+
+<script>
+// import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+// import IconChevron from '@carbon/icons-vue/es/chevron--down/20';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+import LinkButton from '@/components/Global/LinkButton';
+import UploadFile from '@/components/Global/UploadFile';
+import ModalUpdateFirmware from './FirmwareModalUpdateFirmware';
+import { BOverlay } from 'bootstrap-vue';
+import { bus } from '@/utilities/bus';
+// import TableRowExpandMixin, {
+//   expandRowLabel,
+// } from '@/components/Mixins/TableRowExpandMixin';
+export default {
+  components: {
+    // IconTrashcan,
+    LinkButton,
+    UploadFile,
+    ModalUpdateFirmware,
+    BOverlay,
+    // IconChevron,
+  },
+  mixins: [BVToastMixin, LoadingBarMixin],
+  props: {
+    isPageDisabled: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      console,
+      file: null,
+      fileErrorTips: '',
+      bmcConfig: 'Keep',
+      bmcConfig2: 'BMC',
+      biosConfig: 'Keep',
+      biosConfig2: 'BIOS',
+      retimerConfig: 'Retimer0',
+      isKeepConfigs: true,
+      updatingFirmware: null,
+      getStatustimerId: undefined,
+      isNeedReboot: false,
+      showOverlay: false,
+      opacity: 0,
+      // expandRowLabel,
+      fields: [
+        {
+          key: 'expandRow',
+          label: '',
+          tdClass: 'table-row-expand',
+        },
+        {
+          key: 'name',
+          label: this.$t('pageFirmware.table.firmwareName'),
+        },
+        {
+          key: 'status',
+          label: this.$t('pageFirmware.table.firmwareStatus'),
+        },
+        {
+          key: 'version',
+          label: this.$t('pageFirmware.table.firmwareVersion'),
+        },
+        {
+          key: 'Action',
+          label: '',
+        },
+      ],
+      retimerOptions: [
+        'Retimer0',
+        'Retimer1',
+        'Retimer2',
+        'Retimer3',
+        'Retimer4',
+        'Retimer5',
+        'Retimer6',
+        'Retimer7',
+        'All',
+      ],
+    };
+  },
+  computed: {
+    allFirmwareStatus() {
+      const allFirmware = this.$store.getters['firmware/allFirmwareStatus'];
+
+      return (
+        allFirmware?.reduce((res, item) => {
+          const [temp] = Object.entries(item);
+          res.push({
+            name: temp[0],
+            ...temp[1],
+            _showDetails:
+              temp[1].UpdateStatus === 'Uploaded' &&
+              (temp[0] === 'BMC' ||
+                temp[0] === 'BIOS' ||
+                temp[0] === 'Retimer'),
+          });
+          return res;
+        }, []) || []
+      );
+    },
+    priorityFirmware() {
+      const pri = this.$store.getters['firmware/priorityFirmware'];
+      return pri?.Priority ?? null;
+    },
+    bmcConfigDisabled() {
+      return (
+        this.isPageDisabled ||
+        this.flagForBmcConfig ||
+        this.flagForPrimaryBmcConfig
+      );
+    },
+    flagForBmcConfig() {
+      return this.priorityFirmware === 1 && this.bmcConfig2 === 'BMC';
+    },
+    flagForPrimaryBmcConfig() {
+      return this.priorityFirmware === 0 && this.bmcConfig2 === 'BMC_Secondary';
+    },
+    // Upgrade the primary from the primary or upgrade the backup from the backup.
+    isPrimaryToPrimaryOrSecondaryToSecondary() {
+      return (
+        (this.priorityFirmware === 0 && this.bmcConfig2 === 'BMC') ||
+        (this.priorityFirmware === 1 && this.bmcConfig2 === 'BMC_Secondary')
+      );
+    },
+    // bmcIsUpdatingfCurrentPartition
+    bmcIsUpdatingfCurrentPartition() {
+      return this.$store.getters['firmware/bmcIsUpdatingfCurrentPartition'];
+    },
+    runningOnSecondaryContainBMC() {
+      if (
+        this.allFirmwareStatus.find(
+          (it) => it.UpdateStatus === 'Uploaded' && it.name === 'BMC'
+        )
+      ) {
+        if (this.bmcConfig2 === 'BMC' || this.bmcConfig2 === 'BMC_Secondary') {
+          return this.priorityFirmware === 1;
+        }
+        return false;
+      }
+      return false;
+    },
+    bmcConfigOptions() {
+      const opts = [
+        {
+          text: this.$t('pageFirmware.primaryImage'),
+          value: 'BMC',
+        },
+        {
+          text: this.$t('pageFirmware.secondaryImage'),
+          value: 'BMC_Secondary',
+        },
+        {
+          text: this.$t('pageFirmware.both'),
+          value: 'Both',
+        },
+      ];
+      const firmwareStatus =
+        this.$store.getters['firmware/allFirmwareStatus'] || [];
+      const bothEnabled = firmwareStatus.some(
+        (it) => it.BMC?.BothUpdateEnable === true
+      );
+      if (!bothEnabled) {
+        opts.splice(2, 1);
+      }
+      return this.priorityFirmware === 1 ? opts.slice(0, 1) : opts;
+    },
+    biosConfigOptions() {
+      const opts = [
+        {
+          text: this.$t('pageFirmware.primaryImage'),
+          value: 'BIOS',
+        },
+        {
+          text: this.$t('pageFirmware.secondaryImage'),
+          value: 'BIOS_Secondary',
+        },
+        {
+          text: this.$t('pageFirmware.both'),
+          value: 'Both',
+        },
+      ];
+      const firmwareStatus =
+        this.$store.getters['firmware/allFirmwareStatus'] || [];
+      const bothEnabled = firmwareStatus.some(
+        (it) => it.BIOS?.BothUpdateEnable === true
+      );
+      if (!bothEnabled) {
+        opts.splice(2, 1);
+      }
+      return opts;
+    },
+    hasUploadedBmc() {
+      return this.allFirmwareStatus.find(
+        (it) => it.UpdateStatus === 'Uploaded' && it.name === 'BMC'
+      );
+    },
+  },
+  watch: {
+    flagForBmcConfig: {
+      immediate: true,
+      handler(newVal) {
+        if (newVal) this.bmcConfig = 'FactoryReset';
+      },
+    },
+    flagForPrimaryBmcConfig: {
+      immediate: true,
+      handler(newVal) {
+        if (newVal) this.bmcConfig = 'Keep';
+      },
+    },
+  },
+  created() {
+    // this.$store.dispatch('firmware/getAllUpdateStatus');
+    this.getFirmwareStatus();
+    setTimeout(() => {
+      this.getFirmwareStatusTimer();
+    }, 5000);
+  },
+  beforeDestroy() {
+    clearTimeout(this.getStatustimerId);
+    this.getStatustimerId = null;
+  },
+  methods: {
+    updatingBmcPartitionName(item) {
+      if (item.name !== 'BMC' || !this.$store.state.firmware.bmcIsUpdating)
+        return '';
+      // When upgrading BMC and BMC's Update progress is null and Update Status is Updated, Updating is displayed
+      if (
+        item.UpdateStatus === 'Updated' &&
+        this.$store.state.firmware.bmcIsUpdating &&
+        item.UpdateProgress === null
+      ) {
+        return 'Updating';
+      }
+      const currentPartition =
+        this.priorityFirmware === 0 ? 'Primary' : 'Secondary';
+      const map = {
+        Primary: 'Secondary',
+        Secondary: 'Primary',
+      };
+      if (this.bmcIsUpdatingfCurrentPartition) {
+        return currentPartition;
+      } else {
+        return map[currentPartition];
+      }
+    },
+    // get FirmwareStatus
+    getFirmwareStatus() {
+      this.loadingVisible(true);
+      this.$store.dispatch('firmware/getAllUpdateStatus').finally(() => {
+        this.loadingVisible(false);
+      });
+    },
+    // timer
+    async getFirmwareStatusTimer() {
+      try {
+        await this.$store.dispatch('firmware/getAllUpdateStatus');
+      } finally {
+        if (this.getStatustimerId !== null) {
+          this.getStatustimerId = setTimeout(() => {
+            this.getFirmwareStatusTimer();
+          }, 5000);
+        }
+      }
+    },
+    loadingVisible(bool) {
+      bool ? this.startLoader() : this.endLoader();
+      this.$root.$emit('set-upload-event', bool);
+    },
+    getUpdateOptions() {
+      const all = [
+        {
+          text: this.$t('pageFirmware.form.updateOptions.keep'),
+          value: 'Keep',
+        },
+        {
+          text: this.$t('pageFirmware.form.updateOptions.clear'),
+          value: 'Clear',
+        },
+        {
+          text: this.$t('pageFirmware.form.updateOptions.partialKeep'),
+          value: 'PartialKeep',
+        },
+        {
+          text: this.$t('pageFirmware.form.updateOptions.factoryReset'),
+          value: 'FactoryReset',
+        },
+      ];
+      // return type === 'BMC' ? all : all.slice(0, 2);
+      return all.slice(0, 2);
+    },
+    uploadImage() {
+      this.loadingVisible(true);
+      const timerId = setTimeout(() => {
+        this.loadingVisible(false);
+        this.errorToast(this.$t('pageFirmware.toast.timeoutUploadMessage'), {
+          title: this.$t('pageFirmware.toast.timeoutUpload'),
+        });
+      }, 1000000);
+      const toastId = 'upload-started-toast' + Date.now();
+      this.infoToast(this.$t('pageFirmware.toast.uploadStartedMessage'), {
+        title: this.$t('pageFirmware.toast.uploadStarted'),
+        timestamp: true,
+        id: toastId,
+      });
+      this.dispatchUpload(timerId, toastId);
+    },
+    dispatchUpload(timerId, toastId) {
+      console.log('file', this.file);
+      this.$store
+        .dispatch('firmware/uploadFirmwareOEM', this.file)
+        .then((res) => {
+          setTimeout(() => {
+            this.$bvToast.hide(toastId);
+          }, 10000);
+          this.file = null;
+          if (!res) return;
+          const oemData = res?.data?.Oem?.Luxshare?.[0];
+          const firmwareKey = oemData ? Object.keys(oemData)[0] : '';
+          const versionStatus = oemData
+            ? oemData[firmwareKey]?.VersionStatus
+            : '';
+          const uploadStatus = oemData
+            ? oemData[firmwareKey]?.UpdateStatus
+            : '';
+          if (uploadStatus === 'Failed') {
+            let errorToast = this.$t('pageFirmware.toast.errorUploadFirmware');
+            if (versionStatus === 'Invalid') {
+              errorToast = this.$t(
+                'pageFirmware.toast.validateFirmwareFailed',
+                {
+                  firmware: firmwareKey,
+                }
+              );
+            } else if (versionStatus === 'Mismatch') {
+              errorToast = this.$t('pageFirmware.toast.firmwareIdMismatch', {
+                firmware: firmwareKey,
+              });
+            }
+            this.errorToast(errorToast);
+          } else {
+            this.successToast(
+              this.$t('pageFirmware.toast.uploadSuccessMessage'),
+              {
+                title: this.$t('pageFirmware.toast.uploadSuccess'),
+                timestamp: true,
+              }
+            );
+          }
+          this.getFirmwareStatus();
+        })
+        .catch(({ message }) => {
+          this.errorToast(message);
+        })
+        .finally(() => {
+          this.loadingVisible(false);
+          clearTimeout(timerId);
+        });
+    },
+    validtedFileType(file) {
+      return new Promise((resolve, reject) => {
+        const fileExtension = file.name.split('.').pop().toLowerCase();
+        if (fileExtension === 'tar') {
+          resolve('');
+          return;
+        }
+        if (fileExtension === 'gz' || fileExtension === 'tgz') {
+          resolve('');
+          return;
+        }
+
+        const temporaryFileReader = new FileReader();
+        temporaryFileReader.onerror = () => {
+          temporaryFileReader.abort();
+          reject(this.$t('pageFirmware.form.invalidFiletype'));
+        };
+        temporaryFileReader.onload = (e) => {
+          const arr = new Uint8Array(e.target.result).subarray(0, 4);
+          let header = '';
+          for (let i = 0; i < arr.length; i++) {
+            header += arr[i].toString(16);
+          }
+          let type = null;
+          switch (header) {
+            case '504b34':
+              type = 'application/x-zip-compressed';
+              break;
+            case '1f8b88':
+              type = 'application/x-gzip';
+              break;
+            default:
+              type = 'unknown';
+              break;
+          }
+          const validateFiletype = [
+            'application/x-gzip',
+            'application/x-zip-compressed',
+            'application/x-tar',
+          ];
+          if (validateFiletype.includes(type)) {
+            resolve('');
+          } else {
+            reject(this.$t('pageFirmware.form.invalidFiletype'));
+          }
+        };
+        temporaryFileReader.readAsArrayBuffer(file);
+      });
+    },
+    formatStatus(item) {
+      if (item.UpdateStatus === 'Updating') {
+        // return <b-progress variant="primary"></b-progress>;
+        return 'Updating - ' + item.UpdateProgress + '%';
+      } else return item.UpdateStatus;
+    },
+    handleUpdate(item) {
+      this.updatingFirmware = item;
+      this.$bvModal.show('modal-update-firmware');
+    },
+    getAllUpdateAction() {
+      if (
+        this.allFirmwareStatus.find(
+          (it) => it.UpdateStatus === 'Uploaded' && it.name === 'BMC'
+        )
+      ) {
+        return this.bmcConfig;
+      } else if (
+        this.allFirmwareStatus.find(
+          (it) => it.UpdateStatus === 'Uploaded' && it.name === 'BIOS'
+        )
+      ) {
+        return this.biosConfig;
+      } else {
+        return undefined;
+      }
+    },
+    getTarget() {
+      if (this.updatingFirmware.name === 'BMC') {
+        return this.bmcConfig2;
+      } else if (this.updatingFirmware.name === 'BIOS') {
+        return this.biosConfig2;
+      } else if (this.updatingFirmware.name === 'Retimer') {
+        return this.retimerConfig;
+      } else {
+        return this.updatingFirmware.name;
+      }
+    },
+    checkIsNeedReboot(isAll) {
+      // Single update: it's BMC and it upgrades the primary from the primary or the backup from the backup.
+      // it's BMC and the Both is selected.
+      return (
+        !isAll &&
+        this.updatingFirmware?.name === 'BMC' &&
+        (this.bmcConfig2 === 'Both' ||
+          this.isPrimaryToPrimaryOrSecondaryToSecondary)
+      );
+    },
+    handleUpdateFirmware(isAll) {
+      this.isNeedReboot = this.checkIsNeedReboot(isAll);
+      if (
+        !isAll &&
+        this.updatingFirmware.name === 'BMC' &&
+        this.bmcConfig2 === 'Both'
+      ) {
+        isAll = true;
+      }
+      if (
+        !isAll &&
+        this.updatingFirmware.name === 'BIOS' &&
+        this.biosConfig2 === 'Both'
+      ) {
+        isAll = true;
+      }
+      this.loadingVisible(true);
+      const timerId = setTimeout(() => {
+        const message = this.$t(
+          'pageFirmware.form.updateFirmware.updateTimeout'
+        );
+        this.updateInfoFail(timerId, message);
+      }, 1500000);
+      this.infoToast(this.$t('pageFirmware.toast.updateStartedMessage'), {
+        title: this.$t('pageFirmware.toast.updateStarted'),
+        timestamp: true,
+      });
+      let updateConfigs;
+      if (isAll) {
+        updateConfigs = {
+          Target: 'All',
+          Action: this.getAllUpdateAction(),
+        };
+      } else {
+        updateConfigs = {
+          Target: this.getTarget(),
+          Action:
+            this.updatingFirmware.name.search(/BIOS|BMC/) !== -1
+              ? this[`${this.updatingFirmware.name.toLowerCase()}Config`]
+              : undefined,
+        };
+      }
+
+      this.$store
+        .dispatch('firmware/startUpdate', updateConfigs)
+        .then((res) => {
+          if (!res) return;
+          if (this.isNeedReboot) {
+            this.$store.commit('firmware/setBmcIsUpdating', true);
+            this.warningToast(
+              this.$t('pageFirmware.toast.bmcUpdatingTips'),
+              {
+                toaster: 'b-toaster-top-center',
+                id: 'bmcUpdatingTips',
+              },
+              true
+            );
+            bus.$on('bmcUpdateComplete', () => {
+              this.$bvToast.hide();
+              this.successToast(
+                this.$t('pageFirmware.toast.bmcUpdateCompleteTips'),
+                {
+                  toaster: 'b-toaster-top-center',
+                },
+                true
+              );
+              this.opacity = 0.1;
+            });
+            let flag = false;
+            bus.$on('updateCurrentPartition', () => {
+              if (flag) return;
+              flag = true;
+              clearTimeout(this.getStatustimerId);
+              this.getStatustimerId = null;
+              this.$store.commit('firmware/setBmcUpdateProgress', 0);
+              this.$store.commit(
+                'firmware/setBmcIsUpdatingfCurrentPartition',
+                true
+              );
+              this.$store.dispatch('firmware/getBmcUpdateStatus');
+            });
+            this.showOverlay = true;
+          }
+          this.updateInfoSuccess(timerId, isAll);
+        })
+        .catch((e) => {
+          const errorCode = e?.response?.status;
+          let errMsg;
+          if (errorCode === 405 && isAll) {
+            errMsg = this.$t('pageFirmware.toast.errorUpdateAsyncAndSync');
+          } else if (errorCode === 503) {
+            errMsg = this.$t('pageFirmware.toast.errorUpdateInPost');
+          } else if (errorCode === 409) {
+            errMsg = this.$t(
+              'pageFirmware.toast.errorUpdateInPeerNodeUpdating'
+            );
+          } else {
+            errMsg = this.$t('pageFirmware.toast.errorUpdateFirmware');
+          }
+          this.updateInfoFail(undefined, errMsg);
+        })
+        .finally(() => {
+          clearTimeout(timerId);
+          this.loadingVisible(false);
+        });
+    },
+    async updateAll() {
+      const isBMCUploaded = this.allFirmwareStatus.some(
+        (it) => it.name === 'BMC' && it.UpdateStatus === 'Uploaded'
+      );
+      const tips =
+        isBMCUploaded && this.allFirmwareStatus.length > 1
+          ? this.$t('pageFirmware.toast.bmcUpdateTips')
+          : null;
+      const confirmTitle = this.$t('pageFirmware.modal.updateAllConfirm');
+
+      try {
+        const value = tips
+          ? await this.$bvModal.msgBoxConfirm(tips, { title: confirmTitle })
+          : await this.$bvModal.msgBoxConfirm(confirmTitle);
+
+        if (value) {
+          this.handleUpdateFirmware(true);
+        }
+      } catch (error) {
+        console.error('Update all error', error);
+      }
+    },
+    updateInfoSuccess(isAll) {
+      this.uploadResultMessage = !isAll
+        ? this.$t('pageFirmware.form.updateFirmware.updateFinished', {
+            type: this.updatingFirmware.name,
+            version: this.updatingFirmware.VersionStatus,
+          })
+        : this.$t('pageFirmware.table.updateComplete');
+      this.infoToast(this.$t('pageFirmware.toast.verifyUpdateMessage'), {
+        title: this.$t('pageFirmware.toast.verifyUpdate'),
+        refreshAction: true,
+      });
+    },
+    updateInfoFail(timerId, message) {
+      this.errorToast(message);
+      timerId && this.loadingVisible(false);
+      timerId && clearTimeout(timerId);
+    },
+    handleDelete(item) {
+      this.$bvModal
+        .msgBoxConfirm(
+          this.$t('pageFirmware.modal.deleteConfirm', {
+            firmware: item.name,
+          })
+        )
+        .then((value) => {
+          if (value) {
+            this.loadingVisible(true);
+            this.$store
+              .dispatch('firmware/deleteFirmware', item.name)
+              .then(() => {
+                this.successToast(
+                  this.$t('pageFirmware.toast.firmwareDeleted', {
+                    firmware: item.name,
+                  }),
+                  {
+                    title: this.$t('pageFirmware.toast.deleteSuccess'),
+                  }
+                );
+                clearTimeout(this.getStatustimerId);
+                this.getStatustimerId = null;
+                this.getFirmwareStatusTimer().finally(() => {
+                  this.loadingVisible(false);
+                });
+              })
+              .catch(() => {
+                this.loadingVisible(false);
+                this.errorToast(
+                  this.$t('pageFirmware.toast.deleteFailed', {
+                    firmware: item.name,
+                  })
+                );
+              });
+          }
+        });
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+$btn-height: 33px;
+.btn-file {
+  height: $btn-height;
+  white-space: nowrap;
+}
+.firmware-overlay {
+  height: 100vh;
+}
+</style>
-- 
2.25.1

