Repository: hashicorp/vault
File: ui/app/components/namespace-picker.js (JavaScript)

Code before:
     *   - path: full namespace path (used to navigate to the namespace)
     *   - label: text displayed inside the namespace picker dropdown (if root, then label = id, else label = path)
     *
     *  Example:
     *   | id         | path                    | label                   |
     *   | ---        | ----                    | -----                   |
     *   | root       | ''                      | 'root'                  |
     *   | namespace1 | 'namespace1'            | 'namespace1'            |
     *   | namespace2 | 'namespace1/namespace2' | 'namespace1/namespace2' |
     */
    return [
      // TODO: Some users (including HDS Admin User) should never see the root namespace. Address this in a followup PR.
      { id: 'root', path: '', label: 'root' },
      ...(namespace?.accessibleNamespaces || []).map((ns) => {
        const parts = ns.split('/');
        return { id: parts[parts.length - 1], path: ns, label: ns };
      }),
    ];
  }

  #getNamespaceLink(location, namespace) {
    const origin = this.#getOrigin(location);
    const encodedNamespace = encodeURIComponent(namespace.path);

    let queryParams = '';
    if (namespace.path !== '') {
      queryParams = `?namespace=${encodedNamespace}`;
    }

    // The full URL/origin is required so that the page is reloaded.
    return `${origin}/ui/vault/dashboard${queryParams}`;
  }

  #getOrigin(location) {
    return location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '');
  }

  @action
  async fetchListCapability() {
    // TODO: Revist. This logic was carried over from previous component implmenetation.
    //  When the user doesn't have this capability, shouldn't we just hide the "Manage" button,
    //  instead of hiding both the "Manage" and "Refresh List" buttons?
    try {
      await this.store.findRecord('capabilities', 'sys/namespaces/');
      this.showAfterOptions = true;
    } catch (e) {
      // If error out on findRecord call it's because you don't have permissions
      // and therefore don't have permission to manage namespaces
      this.showAfterOptions = false;
    }
  }

Review comment (suggestion):
This may not be really necessary as you convert to TS and have stronger typing, but you could check 

```suggestion
  if (!isEmpty(namespace.path)) {
```
to guard against `undefined` or `null` as well.

Code after:
  // TODO make private when converting from js to ts
  #getOptions(namespace) {
    /* Each namespace option has 3 properties: { id, path, and label }
     *   - id: node / namespace name (displayed when the namespace picker is closed)
     *   - path: full namespace path (used to navigate to the namespace)
     *   - label: text displayed inside the namespace picker dropdown (if root, then label = id, else label = path)
     *
     *  Example:
     *   | id         | path                    | label                   |
     *   | ---        | ----                    | -----                   |
     *   | root       | ''                      | 'root'                  |
     *   | namespace1 | 'namespace1'            | 'namespace1'            |
     *   | namespace2 | 'namespace1/namespace2' | 'namespace1/namespace2' |
     */
    return [
      // TODO: Some users (including HDS Admin User) should never see the root namespace. Address this in a followup PR.
      { id: 'root', path: '', label: 'root' },
      ...(namespace?.accessibleNamespaces || []).map((ns) => {
        const parts = ns.split('/');
        return { id: parts[parts.length - 1], path: ns, label: ns };
      }),
    ];
  }

  // TODO make private when converting from js to ts
  #getNamespaceLink(location, namespace) {
    const origin = this.#getOrigin(location);

    let queryParams = '';
    if (!isEmpty(namespace.path)) {
      const encodedNamespace = encodeURIComponent(namespace.path);
      queryParams = `?namespace=${encodedNamespace}`;
    }

    // The full URL/origin is required so that the page is reloaded.
    return `${origin}/ui/vault/dashboard${queryParams}`;
  }

  // TODO make private when converting from js to ts
  #getOrigin(location) {
    return location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '');
  }

  @action
  async fetchListCapability() {
    // TODO: Revist. This logic was carried over from previous component implmenetation.
    //  When the user doesn't have this capability, shouldn't we just hide the "Manage" button,
    //  instead of hiding both the "Manage" and "Refresh List" buttons?
    try {
      await this.store.findRecord('capabilities', 'sys/namespaces/');
      this.showAfterOptions = true;