chromium/ash/webui/shimless_rma/resources/reimaging_device_information_page.ts

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import './shimless_rma_shared.css.js';
import './base_page.js';
import './icons.html.js';
import 'chrome://resources/ash/common/cr_elements/icons.html.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';

import {CrContainerShadowMixin} from 'chrome://resources/ash/common/cr_elements/cr_container_shadow_mixin.js';
import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
import {assert} from 'chrome://resources/js/assert.js';
import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getShimlessRmaService} from './mojo_interface_provider.js';
import {getTemplate} from './reimaging_device_information_page.html.js';
import {FeatureLevel, ShimlessRmaServiceInterface, StateResult} from './shimless_rma.mojom-webui.js';
import {disableNextButton, enableNextButton, focusPageTitle} from './shimless_rma_util.js';

/**
 * @fileoverview
 * 'reimaging-device-information-page' allows the user to update important
 * device information if necessary.
 */

const ReimagingDeviceInformationPageBase =
    I18nMixin(CrContainerShadowMixin(PolymerElement));

/**
 * Supported options for IsChassisBranded and HwComplianceVersion questions.
 */
export enum BooleanOrDefaultOptions {
  DEFAULT = 'default',
  YES = 'yes',
  NO = 'no',
}

export class ReimagingDeviceInformationPage extends
    ReimagingDeviceInformationPageBase {
  static get is() {
    return 'reimaging-device-information-page' as const;
  }

  static get template() {
    return getTemplate();
  }

  static get observers() {
    return [
      'updateNextButtonDisabledState(serialNumber, skuIndex, regionIndex,' +
          ' customLabelIndex, isChassisBranded, hwComplianceVersion,' +
          ' featureLevel)',
    ];
  }

  static get properties() {
    return {

      /**
       * Set by shimless_rma.ts.
       */
      allButtonsDisabled: Boolean,

      disableResetSerialNumber: {
        type: Boolean,
        computed: 'getDisableResetSerialNumber(originalSerialNumber,' +
            'serialNumber, allButtonsDisabled)',
      },

      disableResetRegion: {
        type: Boolean,
        computed: 'getDisableResetRegion(originalRegionIndex, regionIndex,' +
            'allButtonsDisabled)',
      },

      disableResetSku: {
        type: Boolean,
        computed: 'getDisableResetSku(originalSkuIndex, skuIndex,' +
            'allButtonsDisabled)',
      },

      disableResetCustomLabel: {
        type: Boolean,
        computed: 'getDisableResetCustomLabel(' +
            'originalCustomLabelIndex, customLabelIndex, allButtonsDisabled)',
      },

      disableResetDramPartNumber: {
        type: Boolean,
        computed: 'getDisableResetDramPartNumber(' +
            'originalDramPartNumber, dramPartNumber, allButtonsDisabled)',
      },

      originalSerialNumber: {
        type: String,
        value: '',
      },

      serialNumber: {
        type: String,
        value: '',
      },

      regions: {
        type: Array,
        value: () => [],
      },

      originalRegionIndex: {
        type: Number,
        value: -1,
      },

      regionIndex: {
        type: Number,
        value: -1,
      },

      skus: {
        type: Array,
        value: () => [],
      },

      originalSkuIndex: {
        type: Number,
        value: -1,
      },

      skuIndex: {
        type: Number,
        value: -1,
      },

      customLabels: {
        type: Array,
        value: () => [],
      },

      originalCustomLabelIndex: {
        type: Number,
        value: 0,
      },

      customLabelIndex: {
        type: Number,
        value: 0,
      },

      originalDramPartNumber: {
        type: String,
        value: '',
      },

      dramPartNumber: {
        type: String,
        value: '',
      },

      featureLevel: {
        type: Number,
        value: FeatureLevel.kRmadFeatureLevelUnsupported,
      },

      /**
       * Used to refer to the enum values in the HTML file.
       */
      booleanOrDefaultOptions: {
        type: Object,
        value: BooleanOrDefaultOptions,
        readOnly: true,
      },

      isChassisBranded: {
        type: String,
        value: BooleanOrDefaultOptions.DEFAULT,
      },

      hwComplianceVersion: {
        type: String,
        value: BooleanOrDefaultOptions.DEFAULT,
      },
    };
  }

  allButtonsDisabled: boolean;
  private shimlessRmaService: ShimlessRmaServiceInterface =
      getShimlessRmaService();
  protected isChassisBranded: string;
  protected hwComplianceVersion: string;
  protected booleanOrDefaultOptions: boolean|null;
  protected featureLevel: number;
  protected originalDramPartNumber: string;
  protected dramPartNumber: string;
  protected customLabelIndex: number;
  protected originalCustomLabelIndex: number;
  protected originalRegionIndex: number;
  protected regionIndex: number;
  protected originalSkuIndex: number;
  protected skuIndex: number;
  protected regions: string[];
  protected skus: bigint[]|string[];
  protected customLabels: string[];
  protected disableResetSerialNumber: boolean;
  protected disableResetRegion: boolean;
  protected disableResetSku: boolean;
  protected disableResetCustomLabel: boolean;
  protected disableResetDramPartNumber: boolean;
  protected originalSerialNumber: string;
  protected serialNumber: string;

  override ready() {
    super.ready();
    this.getOriginalSerialNumber();
    this.getOriginalRegionAndRegionList();
    this.getOriginalSkuAndSkuList();
    this.getOriginalCustomLabelAndCustomLabelList();
    this.getOriginalDramPartNumber();
    this.getOriginalFeatureLevel();

    focusPageTitle(this);
  }

  private allInformationIsValid(): boolean {
    const complianceQuestionsHaveDefaultValues =
        this.isChassisBranded === BooleanOrDefaultOptions.DEFAULT ||
        this.hwComplianceVersion === BooleanOrDefaultOptions.DEFAULT;
    if (this.areComplianceQuestionsShown() &&
        complianceQuestionsHaveDefaultValues) {
      return false;
    }
    return (this.serialNumber !== '') && (this.skuIndex >= 0) &&
        (this.regionIndex >= 0) && (this.customLabelIndex >= 0);
  }

  private updateNextButtonDisabledState(): void {
    const disabled = !this.allInformationIsValid();
    if (disabled) {
      disableNextButton(this);
    } else {
      enableNextButton(this);
    }
  }

  private getOriginalSerialNumber(): void {
    this.shimlessRmaService.getOriginalSerialNumber().then((result) => {
      this.originalSerialNumber = result.serialNumber;
      this.serialNumber = this.originalSerialNumber;
    });
  }

  private getOriginalRegionAndRegionList(): void {
    this.shimlessRmaService.getOriginalRegion()
        .then((result: {regionIndex: number}) => {
          this.originalRegionIndex = result.regionIndex;
          return this.shimlessRmaService.getRegionList();
        })
        .then((result: {regions: string[]}) => {
          this.regions = result.regions;
          this.regionIndex = this.originalRegionIndex;

          // Need to wait for the select options to render before setting the
          // selected index.
          afterNextRender(this, () => {
            const regionSelect: HTMLSelectElement|null =
                this.shadowRoot!.querySelector('#regionSelect');
            assert(regionSelect);
            regionSelect.selectedIndex = this.regionIndex;
          });
        });
  }

  private getOriginalSkuAndSkuList(): void {
    this.shimlessRmaService.getOriginalSku()
        .then((result: {skuIndex: number}) => {
          this.originalSkuIndex = result.skuIndex;
          return this.shimlessRmaService.getSkuList();
        })
        .then((result: {skus: bigint[]}) => {
          this.skus = result.skus;
          this.skuIndex = this.originalSkuIndex;
          return this.shimlessRmaService.getSkuDescriptionList();
        })
        .then((result: {skuDescriptions: string[]}) => {
          // The SKU description list can be empty.
          if (this.skus.length === result.skuDescriptions.length) {
            this.skus = this.skus.map(
                (sku, index) => `${sku}: ${result.skuDescriptions[index]}`);
          }

          // Need to wait for the select options to render before setting the
          // selected index.
          afterNextRender(this, () => {
            const skuSelect: HTMLSelectElement|null =
                this.shadowRoot!.querySelector('#skuSelect');
            assert(skuSelect);
            skuSelect.selectedIndex = this.skuIndex;
          });
        });
  }

  private getOriginalCustomLabelAndCustomLabelList(): void {
    this.shimlessRmaService.getOriginalCustomLabel()
        .then((result: {customLabelIndex: number}) => {
          this.originalCustomLabelIndex = result.customLabelIndex;
          return this.shimlessRmaService.getCustomLabelList();
        })
        .then((result: {customLabels: string[]}) => {
          this.customLabels = result.customLabels;
          const blankIndex = this.customLabels.indexOf('');
          if (blankIndex >= 0) {
            this.customLabels[blankIndex] =
                this.i18n('confirmDeviceInfoEmptyCustomLabelLabel');
            if (this.originalCustomLabelIndex < 0) {
              this.originalCustomLabelIndex = blankIndex;
            }
          }
          this.customLabelIndex = this.originalCustomLabelIndex;

          // Need to wait for the select options to render before setting the
          // selected index.
          afterNextRender(this, () => {
            const customLabelSelect: HTMLSelectElement|null =
                this.shadowRoot!.querySelector('#customLabelSelect');
            assert(customLabelSelect);
            customLabelSelect!.selectedIndex = this.customLabelIndex;
          });
        });
  }

  private getOriginalDramPartNumber(): void {
    this.shimlessRmaService.getOriginalDramPartNumber().then(
        (result: {dramPartNumber: string}) => {
          this.originalDramPartNumber = result.dramPartNumber;
          this.dramPartNumber = this.originalDramPartNumber;
        });
  }

  private getOriginalFeatureLevel(): void {
    this.shimlessRmaService.getOriginalFeatureLevel().then(
        (result: {originalFeatureLevel: FeatureLevel}) => {
          this.featureLevel = result.originalFeatureLevel;
        });
  }

  protected getDisableResetSerialNumber(): boolean {
    return this.originalSerialNumber === this.serialNumber ||
        this.allButtonsDisabled;
  }

  protected getDisableResetRegion(): boolean {
    return this.originalRegionIndex === this.regionIndex ||
        this.allButtonsDisabled;
  }

  protected getDisableResetSku(): boolean {
    return this.originalSkuIndex === this.skuIndex || this.allButtonsDisabled;
  }

  protected getDisableResetCustomLabel(): boolean {
    return this.originalCustomLabelIndex === this.customLabelIndex ||
        this.allButtonsDisabled;
  }

  protected getDisableResetDramPartNumber(): boolean {
    return this.originalDramPartNumber === this.dramPartNumber ||
        this.allButtonsDisabled;
  }

  protected onSelectedRegionChange(_e: Event): void {
    const regionSelect: HTMLSelectElement|null =
        this.shadowRoot!.querySelector('#regionSelect');
    assert(regionSelect);
    this.regionIndex = regionSelect.selectedIndex;
  }

  protected onSelectedSkuChange(_e: Event): void {
    const skuSelect: HTMLSelectElement|null =
        this.shadowRoot!.querySelector('#skuSelect');
    assert(skuSelect);
    this.skuIndex = skuSelect.selectedIndex;
  }

  protected onSelectedCustomLabelChange(_e: Event): void {
    const customLabelSelect: HTMLSelectElement|null =
        this.shadowRoot!.querySelector('#customLabelSelect');
    assert(customLabelSelect);
    this.customLabelIndex = customLabelSelect.selectedIndex;
  }

  protected onResetSerialNumberButtonClicked(_e: Event): void {
    this.serialNumber = this.originalSerialNumber;
  }

  protected onResetRegionButtonClicked(_e: Event): void {
    const regionSelect: HTMLSelectElement|null =
        this.shadowRoot!.querySelector('#regionSelect');
    assert(regionSelect);
    this.regionIndex = this.originalRegionIndex;
    regionSelect.selectedIndex = this.regionIndex;
  }

  protected onResetSkuButtonClicked(_e: Event): void {
    const skuSelect: HTMLSelectElement|null =
        this.shadowRoot!.querySelector('#skuSelect');
    assert(skuSelect);
    this.skuIndex = this.originalSkuIndex;
    skuSelect.selectedIndex = this.skuIndex;
  }

  protected onResetCustomLabelButtonClicked(_e: Event): void {
    const customLabelSelect: HTMLSelectElement|null =
        this.shadowRoot!.querySelector('#customLabelSelect');
    assert(customLabelSelect);
    this.customLabelIndex = this.originalCustomLabelIndex;
    customLabelSelect.selectedIndex = this.customLabelIndex;
  }

  protected onResetDramPartNumberButtonClicked(_e: Event): void {
    this.dramPartNumber = this.originalDramPartNumber;
  }

  protected onIsChassisBrandedChange(_e: Event): void {
    const isChassisBranded: HTMLSelectElement|null =
        this.shadowRoot!.querySelector('#isChassisBranded');
    assert(isChassisBranded);
    this.isChassisBranded = isChassisBranded.value;
  }

  protected onDoesMeetRequirementsChange(_e: Event): void {
    const doesMeetRequirements: HTMLSelectElement|null =
        this.shadowRoot!.querySelector('#doesMeetRequirements');
    assert(doesMeetRequirements);
    this.hwComplianceVersion = doesMeetRequirements.value;
  }

  onNextButtonClick(): Promise<{stateResult: StateResult}> {
    if (!this.allInformationIsValid()) {
      return Promise.reject(new Error('Some required information is not set'));
    } else {
      let isChassisBranded = false;
      let hwComplianceVersion = 0;

      if (this.areComplianceQuestionsShown()) {
        // Convert isChassisBranded to boolean value for mojo.
        isChassisBranded =
            this.isChassisBranded === BooleanOrDefaultOptions.YES;

        // Convert hwComplianceVersion_ to correct value for mojo.
        const HARDWARE_COMPLIANT = 1;
        const HARDWARE_NOT_COMPLIANT = 0;
        hwComplianceVersion =
            this.hwComplianceVersion === BooleanOrDefaultOptions.YES ?
            HARDWARE_COMPLIANT :
            HARDWARE_NOT_COMPLIANT;
      }

      return this.shimlessRmaService.setDeviceInformation(
          this.serialNumber, this.regionIndex, this.skuIndex,
          this.customLabelIndex, this.dramPartNumber, isChassisBranded,
          hwComplianceVersion);
    }
  }

  private shouldShowComplianceSection(): boolean {
    return this.featureLevel !== FeatureLevel.kRmadFeatureLevelUnsupported;
  }

  private isComplianceStatusKnown(): boolean {
    return this.featureLevel !== FeatureLevel.kRmadFeatureLevelUnsupported &&
        this.featureLevel !== FeatureLevel.kRmadFeatureLevelUnknown;
  }

  private areComplianceQuestionsShown(): boolean {
    return this.shouldShowComplianceSection() &&
        !this.isComplianceStatusKnown();
  }

  private getComplianceStatusString(): string {
    const deviceIsCompliant =
        this.featureLevel >= FeatureLevel.kRmadFeatureLevel1;
    return deviceIsCompliant ? this.i18n('confirmDeviceInfoDeviceCompliant') :
                               this.i18n('confirmDeviceInfoDeviceNotCompliant');
  }
}

declare global {
  interface HTMLElementTagNameMap {
    [ReimagingDeviceInformationPage.is]: ReimagingDeviceInformationPage;
  }
}

customElements.define(
    ReimagingDeviceInformationPage.is, ReimagingDeviceInformationPage);