Repository: hashicorp/vault
File: ui/app/components/totp-edit.js (JavaScript)

Code before:
  get groups() {
    const { generate } = this.args.model;

    const groups = {
      'TOTP Code Options': ['algorithm', 'digits', 'period'],
    };

    if (generate) {
      groups['Provider Options'] = ['keySize', 'skew', 'qrSize'];
    }

    return groups;
  }

  get mode() {
    return this.args.mode || 'show';
  }

  transitionToRoute() {
    this.router.transitionTo(...arguments);
  }

  @action
  reset() {
    this.args.model.unloadRecord();
    this.transitionToRoute(SHOW_ROUTE, this.args.model.name);
  }

  @action
  async deleteKey() {
    try {
      await this.args.model.destroyRecord();
      this.transitionToRoute(LIST_ROOT_ROUTE);
    } catch (err) {
      // err will display via model state
      return;
    }
    this.flashMessages.success('Successfully deleted key');
  }

  createKey = task(
    waitFor(async (event) => {
      event.preventDefault();
      const { isValid, state, invalidFormMessage } = this.args.model.validate();
      this.modelValidations = isValid ? null : state;
      this.invalidFormAlert = invalidFormMessage;

      if (!isValid) return;
      try {
        const allFields = [...this.keyFormFields, ...Object.values(this.groups).flat()];
        await this.args.model.save({

Review comment (bug):
This _shouldn't_ be necessary anymore with some internal ember data improvements in recent upgrades.. Mostly wondering if this may cause weirdness sometimes because we're unloading a record and then relying on that record to transition.

one way to make this a little more resilient (unloadRecord bugs can be tricky, so we can leave that bit for now) is saving the name as a const before we unload

```suggestion
    const { name } = this.args.model
    this.args.model.unloadRecord();
    this.transitionToRoute(SHOW_ROUTE, name);
```


Code after:
  get groups() {
    const { generate } = this.args.model;

    const groups = {
      'TOTP Code Options': ['algorithm', 'digits', 'period'],
    };

    if (generate) {
      groups['Provider Options'] = ['keySize', 'skew', 'qrSize'];
    }

    return groups;
  }

  transitionToRoute() {
    this.router.transitionTo(...arguments);
  }

  @action
  reset() {
    const { name } = this.args.model;
    this.args.model.unloadRecord();
    this.transitionToRoute(SHOW_ROUTE, name);
  }

  @action
  async deleteKey() {
    try {
      await this.args.model.destroyRecord();
      this.transitionToRoute(LIST_ROOT_ROUTE);
    } catch (err) {
      // err will display via model state
      return;
    }
    this.flashMessages.success(`${this.args.model.id} was successfully deleted.`);
  }

  createKey = task(
    waitFor(async (event) => {
      event.preventDefault();
      const { isValid, state, invalidFormMessage } = this.args.model.validate();
      this.modelValidations = isValid ? null : state;
      this.invalidFormAlert = invalidFormMessage;

      if (!isValid) return;
      try {
        const allFields = [...this.defaultKeyFormFields, ...Object.values(this.groups).flat()];
        await this.args.model.save({
          adapterOptions: {
            keyFormFields: allFields,
          },