From 3dded0a7bc20ae6399365da840612c65a40b5219 Mon Sep 17 00:00:00 2001 From: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:27:02 -0700 Subject: [PATCH] Llb/side nav refactor (#2763) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 scope picker component (#2646) * feat: 🎸 scope picker component --------- Co-authored-by: lisbet-alvarez Co-authored-by: Zhihe Li * refactor: πŸ’‘ use hds side nav w/ hds frame instead of rose (#2688) * refactor: πŸ’‘ use hds side nav w/ hds frame instead of rose * test: πŸ’ clean up selectors * fix: πŸ› specificity of style override * refactor: πŸ’‘ use hds side nav list instead of target and portal * refactor: πŸ’‘ revert to using portal component & update scss * docs: ✏️ update comment * refactor: πŸ’‘ use custom class names for containers * refactor: πŸ’‘ pr feedback (use @hasSidebar arg) * refactor: πŸ’‘ fix onboarding page & fix settings title * refactor: πŸ’‘ remove unneeded template file (pr feedback) * test: πŸ’ fix tests due to merge conflicts * test: πŸ’ add missing test selector due to merge conflict * test: πŸ’ fix more broken tests due to merge conflicts * chore: πŸ€– update yarn.lock & update hds version to 4.18.1 * chore: πŸ€– fix e2e test cases due to side nav refactor (#2751) * chore: πŸ€– fix e2e test cases due to side nav refactor * test: πŸ’ fix e2e test for desktop --------- Co-authored-by: lisbet-alvarez Co-authored-by: Zhihe Li --- addons/core/translations/en-us.yaml | 10 +- addons/rose/addon/styles/hds/overrides.scss | 17 + .../app/styles/rose/components/_frame.scss | 13 +- addons/rose/package.json | 2 +- e2e-tests/admin/pages/aliases.js | 7 +- e2e-tests/admin/pages/auth-methods.js | 6 +- e2e-tests/admin/pages/credential-stores.js | 6 +- e2e-tests/admin/pages/groups.js | 2 +- e2e-tests/admin/pages/host-catalogs.js | 2 +- e2e-tests/admin/pages/login.js | 5 +- e2e-tests/admin/pages/orgs.js | 2 +- e2e-tests/admin/pages/projects.js | 2 +- e2e-tests/admin/pages/roles.js | 2 +- e2e-tests/admin/pages/sessions.js | 2 +- e2e-tests/admin/pages/targets.js | 18 +- e2e-tests/admin/pages/users.js | 2 +- e2e-tests/admin/tests/alias-ent.spec.js | 6 +- e2e-tests/admin/tests/alias.spec.js | 4 +- .../admin/tests/auth-method-ldap.spec.js | 6 +- .../tests/auth-method-oidc-vault.spec.js | 13 +- .../admin/tests/auth-method-password.spec.js | 4 +- .../dynamic-host-catalog-aws-ent.spec.js | 4 +- .../tests/dynamic-host-catalog-aws.spec.js | 4 +- e2e-tests/admin/tests/global-settings.spec.js | 2 +- e2e-tests/admin/tests/pagination.spec.js | 2 +- .../tests/session-recording-aws-ent.spec.js | 11 +- .../tests/session-recording-minio-ent.spec.js | 8 +- e2e-tests/admin/tests/target-ent.spec.js | 2 +- e2e-tests/admin/tests/target.spec.js | 2 +- e2e-tests/admin/tests/worker-ent.spec.js | 2 +- e2e-tests/admin/tests/worker-tags.spec.js | 4 +- e2e-tests/admin/tests/worker.spec.js | 2 +- .../components/roles/role/actions/index.hbs | 2 +- .../app/components/scope-picker/index.hbs | 99 +++ ui/admin/app/components/scope-picker/index.js | 48 + ui/admin/app/controllers/application.js | 8 + ui/admin/app/controllers/scopes/index.js | 13 - ui/admin/app/controllers/scopes/scope.js | 1 + ui/admin/app/routes/account.js | 9 +- ui/admin/app/routes/scopes/scope.js | 24 - ui/admin/app/styles/app.scss | 15 +- .../app/templates/account/change-password.hbs | 48 +- ui/admin/app/templates/application.hbs | 193 ++-- ui/admin/app/templates/onboarding/index.hbs | 9 +- ui/admin/app/templates/onboarding/success.hbs | 11 +- ui/admin/app/templates/scopes/index.hbs | 30 - ui/admin/app/templates/scopes/scope.hbs | 417 +++++---- .../app/templates/scopes/scope/-sidebar.hbs | 23 - .../scopes/scope/aliases/alias/index.hbs | 2 +- .../templates/scopes/scope/authenticate.hbs | 9 +- .../scope/targets/target/manage-alias.hbs | 2 +- .../accounts/change-password-test.js | 12 +- .../tests/acceptance/accounts/selectors.js | 3 - .../tests/acceptance/aliases/delete-test.js | 4 +- .../tests/acceptance/aliases/list-test.js | 3 +- .../tests/acceptance/aliases/update-test.js | 3 +- .../tests/acceptance/authentication-test.js | 10 +- ui/admin/tests/acceptance/develop-test.js | 11 +- ui/admin/tests/acceptance/policy/list-test.js | 2 +- .../tests/acceptance/roles/create-test.js | 4 +- .../tests/acceptance/roles/principals-test.js | 10 +- .../acceptance/roles/project-scope-test.js | 2 +- ui/admin/tests/acceptance/scopes-test.js | 19 +- ui/admin/tests/acceptance/scopes/list-test.js | 17 +- .../session-recordings/list-test.js | 8 +- .../tests/acceptance/sessions/list-test.js | 2 +- .../acceptance/storage-buckets/delete-test.js | 2 +- .../acceptance/storage-buckets/list-test.js | 4 +- .../brokered-credential-sources-test.js | 8 +- .../acceptance/targets/host-sources-test.js | 6 +- ...ted-application-credential-sources-test.js | 8 +- .../tests/acceptance/targets/list-test.js | 5 +- .../acceptance/targets/manage-alias-test.js | 2 +- ui/admin/tests/helpers/selectors.js | 9 +- .../components/scope-picker/index-test.js | 204 +++++ yarn.lock | 837 +++++++++++++++--- 76 files changed, 1621 insertions(+), 711 deletions(-) create mode 100644 ui/admin/app/components/scope-picker/index.hbs create mode 100644 ui/admin/app/components/scope-picker/index.js delete mode 100644 ui/admin/app/controllers/scopes/index.js delete mode 100644 ui/admin/app/templates/scopes/index.hbs delete mode 100644 ui/admin/app/templates/scopes/scope/-sidebar.hbs create mode 100644 ui/admin/tests/integration/components/scope-picker/index-test.js diff --git a/addons/core/translations/en-us.yaml b/addons/core/translations/en-us.yaml index 1850af1bf..861961573 100644 --- a/addons/core/translations/en-us.yaml +++ b/addons/core/translations/en-us.yaml @@ -18,9 +18,7 @@ titles: home: Home iam: IAM new: New - resources: Resources details: Details - general: General overview: Overview settings: Settings empty-set: No Items Yet @@ -46,6 +44,11 @@ titles: days: '{days, plural, =1 {# day} other {# days}}' workers: Workers no-worker-filter: No worker filter added + scope-level: '{scope} Level' + scope-iam: '{scope} IAM' + project-actions: Project Actions + user-menu: User Menu + back-link: 'Back to {scope}' descriptions: empty-set: There are no items to display yet. You may be able to add items or try back later. cluster-url-initialization: To get started, please enter your cluster URL @@ -57,6 +60,9 @@ descriptions: provider: This is the provider for this host catalog. The format of the host set filters are specific to each provider. no-search-results: Sorry, we weren't able to find any {resource} for "{query}". no-filter-results: Sorry, we weren’t able to find any {resource} within the filtered parameters. + and-more: ...and more + view-all-orgs: 'View all orgs ({total})' + view-all-projects: 'View all projects ({total})' questions: delete-confirm: Are you sure you want to delete this resource? remove-confirm: Are you sure you want to remove this association? diff --git a/addons/rose/addon/styles/hds/overrides.scss b/addons/rose/addon/styles/hds/overrides.scss index 4d645d135..b6fb09825 100644 --- a/addons/rose/addon/styles/hds/overrides.scss +++ b/addons/rose/addon/styles/hds/overrides.scss @@ -53,3 +53,20 @@ form { color: inherit; } } + +// TODO: This is a temporary fix that ensures changing the color theme won't alter the dropdown colors. +.app-frame-sidebar-style-overrides .side-nav-style-overrides { + .hds-dropdown-toggle-button, + .hds-dropdown-toggle-icon { + background: var(--token-side-nav-color-surface-primary); + + &:hover { + background: var(--token-side-nav-color-surface-interactive-hover); + } + + &:active, + &:focus { + background: var(--token-side-nav-color-surface-primary); + } + } +} diff --git a/addons/rose/app/styles/rose/components/_frame.scss b/addons/rose/app/styles/rose/components/_frame.scss index c4afa29e6..dc79aec59 100644 --- a/addons/rose/app/styles/rose/components/_frame.scss +++ b/addons/rose/app/styles/rose/components/_frame.scss @@ -7,7 +7,7 @@ @use '../utilities/type'; @use '../variables/media'; -$l: sizing.rems(l); // 16 +$l: sizing.rems(l); // 24 $m: sizing.rems(m); // 16 $s: sizing.rems(s); // 12 $xs: sizing.rems(xs); // 8 @@ -36,11 +36,22 @@ $xxxxs: sizing.rems(xxxxs); // 1 border-bottom: $xxxxs solid var(--header-border-color); border-top-left-radius: $xxxs; border-top-right-radius: $xxxs; + display: flex; + align-items: center; + column-gap: $xs; + + h2 { + margin: $m 0; + } } .rose-frame-body { $body-padding: $l; padding: $body-padding; + + p { + margin-bottom: $m; + } } } diff --git a/addons/rose/package.json b/addons/rose/package.json index 2a9e48e94..f850919bc 100644 --- a/addons/rose/package.json +++ b/addons/rose/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@embroider/test-setup": "^2.1.1", - "@hashicorp/design-system-components": "^4.13.0", + "@hashicorp/design-system-components": "^4.18.1", "codemirror": "5.65.7", "ember-auto-import": "^2.10.0", "ember-cli-babel": "^8.2.0", diff --git a/e2e-tests/admin/pages/aliases.js b/e2e-tests/admin/pages/aliases.js index e384a9382..c9012016c 100644 --- a/e2e-tests/admin/pages/aliases.js +++ b/e2e-tests/admin/pages/aliases.js @@ -15,10 +15,13 @@ export class AliasesPage extends BaseResourcePage { * @param {string} targetId ID of the target * @returns Name of the alias */ - async createAliasForTarget(alias, targetId) { + async createAliasForTarget(alias, targetId, orgName) { const aliasName = 'Alias ' + nanoid(); - await this.page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await this.page + .getByRole('link', { name: `Back to ${orgName}`, exact: true }) + .click(); + await this.page.getByRole('link', { name: 'Orgs' }).click(); await this.page.getByRole('link', { name: 'Aliases' }).click(); await expect( this.page diff --git a/e2e-tests/admin/pages/auth-methods.js b/e2e-tests/admin/pages/auth-methods.js index b1d40c71d..7371af69b 100644 --- a/e2e-tests/admin/pages/auth-methods.js +++ b/e2e-tests/admin/pages/auth-methods.js @@ -16,7 +16,7 @@ export class AuthMethodsPage extends BaseResourcePage { async createPasswordAuthMethod() { const authMethodName = 'Auth Method ' + nanoid(); await this.page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await this.page.getByRole('button', { name: 'New' }).click(); @@ -43,7 +43,7 @@ export class AuthMethodsPage extends BaseResourcePage { async createOidcAuthMethod(issuer, clientId, clientSecret, boundaryAddr) { const authMethodName = 'Auth Method ' + nanoid(); await this.page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await this.page.getByRole('button', { name: 'New' }).click(); @@ -130,7 +130,7 @@ export class AuthMethodsPage extends BaseResourcePage { */ async makeAuthMethodPrimary() { await this.page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await this.page.getByLabel('Manage').click(); diff --git a/e2e-tests/admin/pages/credential-stores.js b/e2e-tests/admin/pages/credential-stores.js index 0eb6b8d6e..e99f6f9da 100644 --- a/e2e-tests/admin/pages/credential-stores.js +++ b/e2e-tests/admin/pages/credential-stores.js @@ -17,7 +17,7 @@ export class CredentialStoresPage extends BaseResourcePage { async createStaticCredentialStore() { const credentialStoreName = 'Credential Store ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Credential Stores' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -47,7 +47,7 @@ export class CredentialStoresPage extends BaseResourcePage { async createVaultCredentialStore(vaultAddr, clientToken) { const credentialStoreName = 'Credential Store ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Credential Stores' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -84,7 +84,7 @@ export class CredentialStoresPage extends BaseResourcePage { ) { const credentialStoreName = 'Credential Store ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Credential Stores' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/pages/groups.js b/e2e-tests/admin/pages/groups.js index 0d6fde530..b15133a61 100644 --- a/e2e-tests/admin/pages/groups.js +++ b/e2e-tests/admin/pages/groups.js @@ -16,7 +16,7 @@ export class GroupsPage extends BaseResourcePage { async createGroup() { const groupName = 'Group ' + nanoid(); await this.page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Groups' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/pages/host-catalogs.js b/e2e-tests/admin/pages/host-catalogs.js index f2a6af1f4..805b984c8 100644 --- a/e2e-tests/admin/pages/host-catalogs.js +++ b/e2e-tests/admin/pages/host-catalogs.js @@ -16,7 +16,7 @@ export class HostCatalogsPage extends BaseResourcePage { async createHostCatalog() { const hostCatalogName = 'Host Catalog ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await expect( diff --git a/e2e-tests/admin/pages/login.js b/e2e-tests/admin/pages/login.js index b6228ce64..618b5d578 100644 --- a/e2e-tests/admin/pages/login.js +++ b/e2e-tests/admin/pages/login.js @@ -18,7 +18,10 @@ export class LoginPage extends BaseResourcePage { await this.page.getByLabel('Password', { exact: true }).fill(password); await this.page.getByRole('button', { name: 'Sign In' }).click(); await expect( - this.page.getByRole('navigation', { name: 'General' }), + this.page.getByRole('navigation', { + // TODO: Uncomment line below once both UIs have been refactored to use HDS side nav. + // name: 'Application local navigation', + }), ).toBeVisible(); await expect(this.page.getByText(loginName)).toBeEnabled(); } diff --git a/e2e-tests/admin/pages/orgs.js b/e2e-tests/admin/pages/orgs.js index 0694f6b53..58776b109 100644 --- a/e2e-tests/admin/pages/orgs.js +++ b/e2e-tests/admin/pages/orgs.js @@ -16,7 +16,7 @@ export class OrgsPage extends BaseResourcePage { async createOrg() { const orgName = 'Org ' + nanoid(); await this.page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Orgs' }) .click(); await this.page.getByRole('link', { name: 'New Org' }).click(); diff --git a/e2e-tests/admin/pages/projects.js b/e2e-tests/admin/pages/projects.js index 05c9b3d05..d689678c0 100644 --- a/e2e-tests/admin/pages/projects.js +++ b/e2e-tests/admin/pages/projects.js @@ -16,7 +16,7 @@ export class ProjectsPage extends BaseResourcePage { async createProject() { const projectName = 'Project ' + nanoid(); await this.page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Projects' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/pages/roles.js b/e2e-tests/admin/pages/roles.js index f9937a2bd..ef4a3e7f4 100644 --- a/e2e-tests/admin/pages/roles.js +++ b/e2e-tests/admin/pages/roles.js @@ -16,7 +16,7 @@ export class RolesPage extends BaseResourcePage { const roleName = 'Role ' + nanoid(); await this.page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Roles' }) .click(); await this.page.getByRole('link', { name: 'New Role' }).click(); diff --git a/e2e-tests/admin/pages/sessions.js b/e2e-tests/admin/pages/sessions.js index a4158536f..230dd7cbf 100644 --- a/e2e-tests/admin/pages/sessions.js +++ b/e2e-tests/admin/pages/sessions.js @@ -14,7 +14,7 @@ export class SessionsPage extends BasePage { */ async waitForSessionToBeVisible(targetName) { await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Sessions' }) .click(); let i = 0; diff --git a/e2e-tests/admin/pages/targets.js b/e2e-tests/admin/pages/targets.js index 7d79e82c7..f05a5db76 100644 --- a/e2e-tests/admin/pages/targets.js +++ b/e2e-tests/admin/pages/targets.js @@ -17,7 +17,7 @@ export class TargetsPage extends BaseResourcePage { async createTarget(port) { const targetName = 'Target ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -44,7 +44,7 @@ export class TargetsPage extends BaseResourcePage { async createTargetWithAddress(address, port) { const targetName = 'Target ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -73,7 +73,7 @@ export class TargetsPage extends BaseResourcePage { async createTargetWithAddressAndAlias(address, port, alias) { const targetName = 'Target ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -109,7 +109,7 @@ export class TargetsPage extends BaseResourcePage { async createTcpTargetWithAddressEnt(address, port) { const targetName = 'Target ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -140,7 +140,7 @@ export class TargetsPage extends BaseResourcePage { async createSshTargetEnt(port) { const targetName = 'Target ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -172,7 +172,7 @@ export class TargetsPage extends BaseResourcePage { async createSshTargetWithAddressEnt(address, port) { const targetName = 'Target ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -206,7 +206,7 @@ export class TargetsPage extends BaseResourcePage { async createSshTargetWithAddressAndAlias(address, port, alias) { const targetName = 'Target ' + nanoid(); await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); @@ -349,7 +349,7 @@ export class TargetsPage extends BaseResourcePage { */ async addBrokeredCredentialsToTarget(targetName, credentialName) { await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await expect( @@ -397,7 +397,7 @@ export class TargetsPage extends BaseResourcePage { */ async addInjectedCredentialsToTarget(targetName, credentialName) { await this.page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await expect( diff --git a/e2e-tests/admin/pages/users.js b/e2e-tests/admin/pages/users.js index 60ee8406b..c3d639a00 100644 --- a/e2e-tests/admin/pages/users.js +++ b/e2e-tests/admin/pages/users.js @@ -16,7 +16,7 @@ export class UsersPage extends BaseResourcePage { async createUser() { const userName = 'User ' + nanoid(); await this.page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Users' }) .click(); await this.page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/tests/alias-ent.spec.js b/e2e-tests/admin/tests/alias-ent.spec.js index 08ecd3f1c..20f7d4384 100644 --- a/e2e-tests/admin/tests/alias-ent.spec.js +++ b/e2e-tests/admin/tests/alias-ent.spec.js @@ -92,7 +92,7 @@ test.describe('Aliases (Enterprise)', () => { // Clear destination from alias await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); @@ -270,10 +270,10 @@ test.describe('Aliases (Enterprise)', () => { alias = 'example.alias.' + nanoid(); const aliasesPage = new AliasesPage(page); - await aliasesPage.createAliasForTarget(alias, targetId); + await aliasesPage.createAliasForTarget(alias, targetId, orgName); connect = await boundaryCli.connectSshToAlias(alias); await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Orgs' }) .click(); await page.getByRole('link', { name: orgName }).click(); diff --git a/e2e-tests/admin/tests/alias.spec.js b/e2e-tests/admin/tests/alias.spec.js index 7bfd759e9..431ccb972 100644 --- a/e2e-tests/admin/tests/alias.spec.js +++ b/e2e-tests/admin/tests/alias.spec.js @@ -75,7 +75,7 @@ test.describe('Aliases', () => { // Clear destination from alias await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); @@ -230,7 +230,7 @@ test.describe('Aliases', () => { alias = 'example.alias.' + nanoid(); const aliasesPage = new AliasesPage(page); - await aliasesPage.createAliasForTarget(alias, targetId); + await aliasesPage.createAliasForTarget(alias, targetId, orgName); await page.getByRole('link', { name: 'Orgs', exact: true }).click(); await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); await page.getByRole('link', { name: orgName }).click(); diff --git a/e2e-tests/admin/tests/auth-method-ldap.spec.js b/e2e-tests/admin/tests/auth-method-ldap.spec.js index 1e535cf8f..abd8fbb2f 100644 --- a/e2e-tests/admin/tests/auth-method-ldap.spec.js +++ b/e2e-tests/admin/tests/auth-method-ldap.spec.js @@ -52,7 +52,7 @@ test( const orgsPage = new OrgsPage(page); orgName = await orgsPage.createOrg(); await page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await page.getByRole('button', { name: 'New' }).click(); @@ -191,7 +191,7 @@ test( // View the LDAP account and verify account attributes await page.getByRole('link', { name: orgName }).click(); await page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await page.getByRole('link', { name: ldapAuthMethodName }).click(); @@ -280,7 +280,7 @@ test( // View the User account and verify attributes await page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Users' }) .click(); await page.getByRole('link', { name: userName }).click(); diff --git a/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js b/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js index 9741cb49a..f441121df 100644 --- a/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js +++ b/e2e-tests/admin/tests/auth-method-oidc-vault.spec.js @@ -32,7 +32,6 @@ test( async ({ page, context, - baseURL, controllerAddr, adminAuthMethodId, adminLoginName, @@ -52,7 +51,7 @@ test( userName, password, email, - baseURL, + controllerAddr, ); policyName = authPolicyName; @@ -71,7 +70,7 @@ test( issuer, clientId, clientSecret, - baseURL, + controllerAddr, ); // Change OIDC Auth Method state to active-public @@ -134,9 +133,7 @@ test( ).toBeVisible(); // Log back in as an admin - // WORKAROUND: Currently, users logging using OIDC don't have a username - // displayed in the UI, so there's no simple locator to access this menu. - await page.locator('details').filter({ hasText: 'Sign Out' }).click(); + await page.getByRole('button', { name: 'User Menu' }).click(); await page.getByRole('button', { name: 'Sign Out' }).click(); await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); await loginPage.login(adminLoginName, adminPassword); @@ -152,7 +149,7 @@ test( .getByText(orgName), ).toBeVisible(); await page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await page.getByRole('link', { name: oidcAuthMethodName }).click(); @@ -241,7 +238,7 @@ test( // View the User account and verify attributes await page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Users' }) .click(); await page diff --git a/e2e-tests/admin/tests/auth-method-password.spec.js b/e2e-tests/admin/tests/auth-method-password.spec.js index 708684eb9..b7ad3e6e6 100644 --- a/e2e-tests/admin/tests/auth-method-password.spec.js +++ b/e2e-tests/admin/tests/auth-method-password.spec.js @@ -75,7 +75,7 @@ test( // Set a new password on the account await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Orgs' }) .click(); await page.getByRole('link', { name: orgName }).click(); @@ -85,7 +85,7 @@ test( .getByText(orgName), ).toBeVisible(); await page - .getByRole('navigation', { name: 'IAM' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Auth Methods' }) .click(); await page.getByRole('link', { name: authMethodName }).click(); diff --git a/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js b/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js index 116569a9c..b3b7658a4 100644 --- a/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js +++ b/e2e-tests/admin/tests/dynamic-host-catalog-aws-ent.spec.js @@ -49,7 +49,7 @@ test.describe('AWS', () => { // Create host catalog const hostCatalogName = 'Host Catalog ' + nanoid(); await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: 'New', exact: true }).click(); @@ -164,7 +164,7 @@ test.describe('AWS', () => { await hostCatalogsPage.createHostCatalog(); const newHostSetName = await hostCatalogsPage.createHostSet(); await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); diff --git a/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js b/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js index 05cb97bb3..1a4b1d525 100644 --- a/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js +++ b/e2e-tests/admin/tests/dynamic-host-catalog-aws.spec.js @@ -49,7 +49,7 @@ test.describe('AWS', () => { // Create host catalog const hostCatalogName = 'Host Catalog ' + nanoid(); await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: 'New', exact: true }).click(); @@ -164,7 +164,7 @@ test.describe('AWS', () => { await hostCatalogsPage.createHostCatalog(); const newHostSetName = await hostCatalogsPage.createHostSet(); await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); diff --git a/e2e-tests/admin/tests/global-settings.spec.js b/e2e-tests/admin/tests/global-settings.spec.js index 1d6f16e3e..2c257ad1c 100644 --- a/e2e-tests/admin/tests/global-settings.spec.js +++ b/e2e-tests/admin/tests/global-settings.spec.js @@ -42,7 +42,7 @@ test( // Navigate to orgs page when done await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Orgs' }) .click(); await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); diff --git a/e2e-tests/admin/tests/pagination.spec.js b/e2e-tests/admin/tests/pagination.spec.js index ab4d4cdbb..970a996d1 100644 --- a/e2e-tests/admin/tests/pagination.spec.js +++ b/e2e-tests/admin/tests/pagination.spec.js @@ -34,7 +34,7 @@ test( await page.getByRole('link', { name: org.name }).click(); await page.getByRole('link', { name: project.name }).click(); await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Targets' }) .click(); await expect( diff --git a/e2e-tests/admin/tests/session-recording-aws-ent.spec.js b/e2e-tests/admin/tests/session-recording-aws-ent.spec.js index f0bb6470a..5316e7737 100644 --- a/e2e-tests/admin/tests/session-recording-aws-ent.spec.js +++ b/e2e-tests/admin/tests/session-recording-aws-ent.spec.js @@ -62,6 +62,9 @@ test( const projectName = await projectsPage.createProject(); // Create storage bucket + await page + .getByRole('link', { name: `Back to ${orgName}`, exact: true }) + .click(); await page.getByRole('link', { name: 'Orgs', exact: true }).click(); const storageBucketsPage = new StorageBucketsPage(page); const storageBucketName = await storageBucketsPage.createStorageBucketAws( @@ -107,6 +110,9 @@ test( await targetsPage.enableSessionRecording(storageBucketName); // Create storage policy in org scope: keep session recordings forever + await page + .getByRole('link', { name: `Back to ${orgName}`, exact: true }) + .click(); await page.getByRole('link', { name: 'Orgs', exact: true }).click(); await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); await page.getByRole('link', { name: orgName }).click(); @@ -146,9 +152,12 @@ test( await boundaryCli.waitForSessionRecording(storageBucket.id); // Play back session recording + await page + .getByRole('link', { name: `Back to ${orgName}`, exact: true }) + .click(); await page.getByRole('link', { name: 'Orgs', exact: true }).click(); await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Session Recordings', exact: true }) .click(); await page diff --git a/e2e-tests/admin/tests/session-recording-minio-ent.spec.js b/e2e-tests/admin/tests/session-recording-minio-ent.spec.js index 92c715549..f4de834c3 100644 --- a/e2e-tests/admin/tests/session-recording-minio-ent.spec.js +++ b/e2e-tests/admin/tests/session-recording-minio-ent.spec.js @@ -115,6 +115,9 @@ test( await targetsPage.enableSessionRecording(storageBucketName); // Create storage policy in org scope: keep session recordings forever + await page + .getByRole('link', { name: `Back to ${orgName}`, exact: true }) + .click(); await page.getByRole('link', { name: 'Orgs', exact: true }).click(); await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); await page.getByRole('link', { name: orgName }).click(); @@ -154,9 +157,12 @@ test( await boundaryCli.waitForSessionRecording(storageBucket.id); // Play back session recording + await page + .getByRole('link', { name: `Back to ${orgName}`, exact: true }) + .click(); await page.getByRole('link', { name: 'Orgs', exact: true }).click(); await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Session Recordings', exact: true }) .click(); await page diff --git a/e2e-tests/admin/tests/target-ent.spec.js b/e2e-tests/admin/tests/target-ent.spec.js index a24152aed..7e2425307 100644 --- a/e2e-tests/admin/tests/target-ent.spec.js +++ b/e2e-tests/admin/tests/target-ent.spec.js @@ -190,7 +190,7 @@ test( // Create another host set await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: hostCatalogName }).click(); diff --git a/e2e-tests/admin/tests/target.spec.js b/e2e-tests/admin/tests/target.spec.js index 527da097a..9b5b45d2b 100644 --- a/e2e-tests/admin/tests/target.spec.js +++ b/e2e-tests/admin/tests/target.spec.js @@ -49,7 +49,7 @@ test( // Create another host set await page - .getByRole('navigation', { name: 'Resources' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: hostCatalogName }).click(); diff --git a/e2e-tests/admin/tests/worker-ent.spec.js b/e2e-tests/admin/tests/worker-ent.spec.js index a1d75f185..b26ab5571 100644 --- a/e2e-tests/admin/tests/worker-ent.spec.js +++ b/e2e-tests/admin/tests/worker-ent.spec.js @@ -14,7 +14,7 @@ test( await page.goto('/'); await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Workers' }) .click(); await page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/e2e-tests/admin/tests/worker-tags.spec.js b/e2e-tests/admin/tests/worker-tags.spec.js index 32687a545..6fc032a45 100644 --- a/e2e-tests/admin/tests/worker-tags.spec.js +++ b/e2e-tests/admin/tests/worker-tags.spec.js @@ -17,7 +17,7 @@ test( await page.goto('/'); await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Workers' }) .click(); await expect( @@ -102,7 +102,7 @@ test( // Check tag in worker dialog await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Workers' }) .click(); await expect( diff --git a/e2e-tests/admin/tests/worker.spec.js b/e2e-tests/admin/tests/worker.spec.js index a45985996..30fe2b251 100644 --- a/e2e-tests/admin/tests/worker.spec.js +++ b/e2e-tests/admin/tests/worker.spec.js @@ -14,7 +14,7 @@ test( await page.goto('/'); await page - .getByRole('navigation', { name: 'General' }) + .getByRole('navigation', { name: 'Application local navigation' }) .getByRole('link', { name: 'Workers' }) .click(); await page.getByRole('link', { name: 'New', exact: true }).click(); diff --git a/ui/admin/app/components/roles/role/actions/index.hbs b/ui/admin/app/components/roles/role/actions/index.hbs index b9ac239b8..4be1034a3 100644 --- a/ui/admin/app/components/roles/role/actions/index.hbs +++ b/ui/admin/app/components/roles/role/actions/index.hbs @@ -8,7 +8,7 @@ {{#if (can 'addPrincipals role' @model)}} {{t 'resources.role.principal.actions.add-principals'}} diff --git a/ui/admin/app/components/scope-picker/index.hbs b/ui/admin/app/components/scope-picker/index.hbs new file mode 100644 index 000000000..a3a504808 --- /dev/null +++ b/ui/admin/app/components/scope-picker/index.hbs @@ -0,0 +1,99 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +}} + + + {{#let this.currentScope as |scope|}} + + {{/let}} + + + {{t 'titles.global'}} + + + {{#if this.scope.orgsList}} + + + + {{/if}} + + {{#each this.truncatedOrgsList as |org|}} + + {{org.displayName}} + + {{/each}} + + {{#if (gt this.scope.orgsList.length 5)}} + + + {{t + 'descriptions.view-all-orgs' + total=this.scope.orgsList.length + }} + {{/if}} + + {{! All project scopes in org are only loaded when current scope is of project type. }} + {{#if this.scope.project}} + + + {{#each this.truncatedProjectsList as |project|}} + + {{project.displayName}} + + {{/each}} + + {{#if (gt this.scope.projectsList.length 5)}} + + + {{t + 'descriptions.view-all-projects' + total=this.scope.projectsList.length + }} + {{/if}} + {{/if}} + \ No newline at end of file diff --git a/ui/admin/app/components/scope-picker/index.js b/ui/admin/app/components/scope-picker/index.js new file mode 100644 index 000000000..ad62181cc --- /dev/null +++ b/ui/admin/app/components/scope-picker/index.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Component from '@glimmer/component'; +import { inject as service } from '@ember/service'; +import orderBy from 'lodash/orderBy'; + +export default class ScopePickerIndexComponent extends Component { + // =services + + @service intl; + @service scope; + @service session; + + // =attributes + + /** + * Returns display name and icon for current scope. + * @type {object} + */ + get currentScope() { + if (this.scope.project) { + return { name: this.scope.project.displayName, icon: 'grid' }; + } else if (this.scope.org.isOrg) { + return { name: this.scope.org.displayName, icon: 'org' }; + } else { + return { name: this.intl.t('titles.global'), icon: 'globe' }; + } + } + + /** + * Returns first five orgs ordered by most recently updated. + * @type {[ScopeModel]} + */ + get truncatedOrgsList() { + return orderBy(this.scope.orgsList, 'updated_time', 'desc').slice(0, 5); + } + + /** + * Returns first five projects ordered by most recently updated. + * @type {[ScopeModel]} + */ + get truncatedProjectsList() { + return orderBy(this.scope.projectsList, 'updated_time', 'desc').slice(0, 5); + } +} diff --git a/ui/admin/app/controllers/application.js b/ui/admin/app/controllers/application.js index 8ed63bcf7..6a554d4d1 100644 --- a/ui/admin/app/controllers/application.js +++ b/ui/admin/app/controllers/application.js @@ -30,6 +30,7 @@ export default class ApplicationController extends Controller { @service features; @service featureEdition; @service flashMessages; + @service router; /** * Returns available themes @@ -39,6 +40,13 @@ export default class ApplicationController extends Controller { return THEMES; } + get showSideNav() { + return ( + this.router.currentRouteName.startsWith('scopes.scope') && + this.session.isAuthenticated + ); + } + // =actions /** diff --git a/ui/admin/app/controllers/scopes/index.js b/ui/admin/app/controllers/scopes/index.js deleted file mode 100644 index 13ea3b901..000000000 --- a/ui/admin/app/controllers/scopes/index.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Controller from '@ember/controller'; -import { service } from '@ember/service'; - -export default class ScopesIndexController extends Controller { - // =services - - @service session; -} diff --git a/ui/admin/app/controllers/scopes/scope.js b/ui/admin/app/controllers/scopes/scope.js index 80a51e631..21ccaca37 100644 --- a/ui/admin/app/controllers/scopes/scope.js +++ b/ui/admin/app/controllers/scopes/scope.js @@ -10,4 +10,5 @@ export default class ScopesScopeController extends Controller { // =services @service session; + @service scope; } diff --git a/ui/admin/app/routes/account.js b/ui/admin/app/routes/account.js index 9d3bc8ed7..a8adeba1a 100644 --- a/ui/admin/app/routes/account.js +++ b/ui/admin/app/routes/account.js @@ -15,9 +15,14 @@ export default class AccountRoute extends Route { // =methods /** - * If arriving here unauthenticated, redirect to index for further processing. + * If arriving here unauthenticated, redirect to index for further processing + * else redirect to change-password route since account route is empty. */ beforeModel() { - if (!this.session.isAuthenticated) this.router.transitionTo('index'); + if (!this.session.isAuthenticated) { + this.router.transitionTo('index'); + } else { + this.router.transitionTo('account.change-password'); + } } } diff --git a/ui/admin/app/routes/scopes/scope.js b/ui/admin/app/routes/scopes/scope.js index c7220cac9..f7705c345 100644 --- a/ui/admin/app/routes/scopes/scope.js +++ b/ui/admin/app/routes/scopes/scope.js @@ -72,29 +72,5 @@ export default class ScopesScopeRoute extends Route { this.scope.project = selectedProject; this.scope.orgsList = orgs; this.scope.projectsList = projects; - this.scopes = { orgs, projects, selectedOrg, selectedProject }; - // Update the controller (if exists), since setupController is only - // called once the first time the route is activated. It is not called - // again on route refreshes. - /* eslint-disable-next-line ember/no-controller-access-in-routes */ - if (this.controller) this.setControllerProperties(this.scopes); - } - - /** - * Adds the scopes hash to the controller context (see `afterModel`). - * @param {Controller} controller - */ - setupController(/* controller */) { - super.setupController(...arguments); - this.setControllerProperties(this.scopes); - } - - /** - * Updates the controller's `scopes`. - * @param {array} scopes - */ - setControllerProperties(scopes) { - /* eslint-disable-next-line ember/no-controller-access-in-routes */ - this.controller.setProperties({ scopes }); } } diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index 2ee969578..eead2cadc 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -173,8 +173,6 @@ // Onboarding styles .create-resources { - display: flex; - align-items: flex-start; width: sizing.rems(xl) * 15; .create-resources-form { @@ -185,7 +183,6 @@ .hds-form-group { margin-top: sizing.rems(l); - justify-content: center; .image-container { display: flex; @@ -1083,3 +1080,15 @@ margin-bottom: 0; } } + +// scope picker +.scope-picker { + &__toggle-button { + width: 100%; + } + + li:has(.indentation), + li.indentation { + margin-left: 1.5rem; + } +} diff --git a/ui/admin/app/templates/account/change-password.hbs b/ui/admin/app/templates/account/change-password.hbs index 39ad90232..d6fd7858c 100644 --- a/ui/admin/app/templates/account/change-password.hbs +++ b/ui/admin/app/templates/account/change-password.hbs @@ -5,32 +5,26 @@ {{page-title (t 'actions.change-password')}} - - -
- - + + - -

{{t 'actions.change-password'}}

-
+ + + <:header> + + {{t + 'actions.change-password' + }} + + <:body> + + + + - - - - -
-
-
-
- - - {{t 'titles.documentation'}} - - - -
\ No newline at end of file + + \ No newline at end of file diff --git a/ui/admin/app/templates/application.hbs b/ui/admin/app/templates/application.hbs index 7374139aa..12585bb5f 100644 --- a/ui/admin/app/templates/application.hbs +++ b/ui/admin/app/templates/application.hbs @@ -5,113 +5,116 @@ {{page-title (app-name)}} - + + + + <:header> + + <:logo> + + + <:actions> + {{#if (feature-flag 'dev-edition-toggle')}} + + - - - - - {{#if this.session.isAuthenticated}} - - - - - - - - {{t 'actions.change-password'}} - - - - - {{t 'actions.deauthenticate'}} - - - - - - - {{#each this.themes as |theme|}} - + {{#each this.featureEdition.editions as |edition|}} + - {{t (concat 'themes.' theme.label)}} - + {{edition}} + {{/each}} - - + - + + {{#each this.featureEdition.licensedFeatures as |feature|}} + + {{feature}} + + {{/each}} + + {{/if}} - {{#if (feature-flag 'dev-edition-toggle')}} - - - + {{else}} + + {{/if}} + + {{t + 'actions.change-password' + }} + + + {{t + 'actions.deauthenticate' + }} + + + + {{#each this.themes as |theme|}} + - {{#each this.featureEdition.editions as |edition|}} - - {{edition}} - - {{/each}} - + {{t (concat 'themes.' theme.label)}} + + {{/each}} + + + + - - - - - {{#each this.featureEdition.licensedFeatures as |feature|}} - - - - {{/each}} - - - - {{/if}} + <:body> + + - + <:footer> + + + + - {{/if}} - - - - - + {{outlet}} - + - + + + {{t 'titles.documentation'}} + + + + {{#if this.flashMessages.queue}}
diff --git a/ui/admin/app/templates/onboarding/index.hbs b/ui/admin/app/templates/onboarding/index.hbs index 5a5bb223e..c78aae327 100644 --- a/ui/admin/app/templates/onboarding/index.hbs +++ b/ui/admin/app/templates/onboarding/index.hbs @@ -3,10 +3,13 @@ SPDX-License-Identifier: BUSL-1.1 }} - - + + <:header> -

{{t 'onboarding.add-target.titles.have-a-target'}}

+ + {{t + 'onboarding.add-target.titles.have-a-target' + }} <:body>

{{t 'onboarding.add-target.descriptions.setup-first-target'}}

diff --git a/ui/admin/app/templates/onboarding/success.hbs b/ui/admin/app/templates/onboarding/success.hbs index 01e8d8915..488950126 100644 --- a/ui/admin/app/templates/onboarding/success.hbs +++ b/ui/admin/app/templates/onboarding/success.hbs @@ -4,10 +4,14 @@ }} {{page-title (t 'titles.success')}} - - + + + <:header> -

{{t 'onboarding.success.titles.connect'}}

+ + {{t + 'onboarding.success.titles.connect' + }} <:body> {{! We have created }} @@ -27,7 +31,6 @@ {{! Download Desktop client }}
-

{{t 'onboarding.success.titles.desktop'}}

{{t 'onboarding.success.descriptions.download'}}

diff --git a/ui/admin/app/templates/scopes/index.hbs b/ui/admin/app/templates/scopes/index.hbs deleted file mode 100644 index 5f7437f28..000000000 --- a/ui/admin/app/templates/scopes/index.hbs +++ /dev/null @@ -1,30 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -}} - -{{outlet}} - -{{#unless @model}} - - -
- - - - - - -
-
- - - {{t 'titles.documentation'}} - - - -
-{{/unless}} \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope.hbs b/ui/admin/app/templates/scopes/scope.hbs index ab94e6edc..4942104e9 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -16,14 +16,14 @@ {{#if @model.isProject}} {{page-title (t 'resources.org.title_plural')}} - {{page-title this.scopes.selectedOrg.displayName}} + {{page-title this.scope.org.displayName}} {{page-title (t 'resources.project.title_plural')}} {{page-title @model.displayName}} {{/if}} - + + {{#if @model.isGlobal}} + {{t 'titles.scope-level' scope=(t 'titles.global')}} - {{#if this.session.isAuthenticated}} - + - {{#if @model.isGlobal}} - - - - {{t 'resources.org.title_plural'}} - + {{#if (can 'navigate scope' @model collection='session-recordings')}} + + {{/if}} - {{#if (can 'navigate scope' @model collection='session-recordings')}} - - - {{t 'resources.session-recording.title_plural'}} - - {{/if}} - {{#if (can 'navigate scope' @model collection='storage-buckets')}} - - - {{t 'resources.storage-bucket.title_plural'}} - - {{/if}} - {{#if (can 'navigate scope' @model collection='policies')}} - - - {{t 'resources.policy.title_plural'}} - - {{/if}} - {{#if (can 'navigate scope' @model collection='aliases')}} - - - {{t 'resources.alias.title_plural'}} - - {{/if}} - {{#if - (and - (can 'navigate worker' @model collection='workers') - (feature-flag 'byow') - ) - }} - - - {{t 'titles.workers'}} - - {{/if}} - - {{/if}} + {{#if (can 'navigate scope' @model collection='storage-buckets')}} + + {{/if}} - {{#if @model.isOrg}} - - {{#if this.session.data.authenticated.isGlobal}} - - - {{t 'resources.org.title_plural'}} - - {{/if}} - - - {{t 'resources.project.title_plural'}} - + {{#if (can 'navigate scope' @model collection='policies')}} + + {{/if}} - {{#if (can 'navigate scope' @model collection='policies')}} - - - {{t 'resources.policy.title_plural'}} - - {{/if}} - + {{#if (can 'navigate scope' @model collection='aliases')}} + + {{/if}} - {{/if}} + {{#if + (and + (can 'navigate worker' @model collection='workers') + (feature-flag 'byow') + ) + }} + + {{/if}} - {{#if @model.isProject}} - - {{#if this.session.data.authenticated.isGlobal}} - - - {{t 'resources.org.title_plural'}} - - {{/if}} - - - {{t 'resources.project.title_plural'}} - - - - - {{#if (can 'navigate model' @model collection='sessions')}} - - - {{t 'resources.session.title_plural'}} - - {{/if}} - {{#if (can 'navigate model' @model collection='targets')}} - - - {{t 'resources.target.title_plural'}} - - {{/if}} - {{#if (can 'navigate model' @model collection='host-catalogs')}} - - - {{t 'resources.host-catalog.title_plural'}} - - {{/if}} - {{#if (can 'navigate model' @model collection='credential-stores')}} - - - {{t 'resources.credential-store.title_plural'}} - - {{/if}} - - {{/if}} - - - {{! Users are available only for org/global scopes }} - {{#unless @model.isProject}} - {{#if (can 'navigate model' @model collection='users')}} - - - {{t 'resources.user.title_plural'}} - - {{/if}} - {{/unless}} - - {{! Groups and roles are available for all scopes }} - {{#if (can 'navigate model' @model collection='groups')}} - - - {{t 'resources.group.title_plural'}} - - {{/if}} - {{#if (can 'navigate model' @model collection='roles')}} - - - {{t 'resources.role.title_plural'}} - - {{/if}} - - {{! Auth methods are available only for org/global scopes }} - {{#unless @model.isProject}} - {{#if (can 'navigate model' @model collection='auth-methods')}} - - - {{t 'resources.auth-method.title_plural'}} - - {{/if}} - {{/unless}} - - - {{#if (and @model.isGlobal (feature-flag 'ssh-session-recording'))}} - {{#if this.session.data.authenticated.isGlobal}} - - - {{t 'resources.global.messages.settings.title'}} - - - {{/if}} - {{/if}} - - {{#if @model.isOrg}} - {{#if this.session.data.authenticated.isGlobal}} - - - {{t 'resources.org.messages.settings.title'}} - - - {{/if}} - {{/if}} - - {{#if @model.isProject}} - - - {{t 'resources.project.messages.settings.title'}} - - - {{/if}} - - + {{t 'titles.scope-iam' scope=(t 'titles.global')}} {{/if}} - -
- {{outlet}} -
-
- - - {{t 'titles.documentation'}} - - - -
\ No newline at end of file + {{#if @model.isOrg}} + {{t + 'titles.scope-level' + scope=(t 'resources.org.title') + }} + + {{#if this.session.data.authenticated.isGlobal}} + + {{/if}} + + + + {{#if (can 'navigate scope' @model collection='policies')}} + + {{/if}} + + {{t + 'titles.scope-iam' + scope=(t 'resources.org.title') + }} + {{/if}} + + {{#if @model.isProject}} + + + {{t 'titles.project-actions'}} + + {{#if (can 'navigate model' @model collection='sessions')}} + + {{/if}} + + {{#if (can 'navigate model' @model collection='targets')}} + + {{/if}} + + {{#if (can 'navigate model' @model collection='host-catalogs')}} + + {{/if}} + + {{#if (can 'navigate model' @model collection='credential-stores')}} + + {{/if}} + + {{t + 'titles.scope-iam' + scope=(t 'resources.project.title') + }} + {{/if}} + + {{! Users are available only for org/global scopes }} + {{#unless @model.isProject}} + {{#if (can 'navigate model' @model collection='users')}} + + {{/if}} + {{/unless}} + + {{! Groups and roles are available for all scopes }} + {{#if (can 'navigate model' @model collection='groups')}} + + {{/if}} + {{#if (can 'navigate model' @model collection='roles')}} + + {{/if}} + + {{! Auth methods are available only for org/global scopes }} + {{#unless @model.isProject}} + {{#if (can 'navigate model' @model collection='auth-methods')}} + + {{/if}} + {{/unless}} + + {{#if (and @model.isGlobal (feature-flag 'ssh-session-recording'))}} + {{#if this.session.data.authenticated.isGlobal}} + {{t 'titles.settings'}} + + + {{/if}} + {{/if}} + + {{#if @model.isOrg}} + {{#if this.session.data.authenticated.isGlobal}} + {{t 'titles.settings'}} + + + {{/if}} + {{/if}} + + {{#if @model.isProject}} + {{t 'titles.settings'}} + + + {{/if}} + + +{{outlet}} \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/-sidebar.hbs b/ui/admin/app/templates/scopes/scope/-sidebar.hbs deleted file mode 100644 index 10151e826..000000000 --- a/ui/admin/app/templates/scopes/scope/-sidebar.hbs +++ /dev/null @@ -1,23 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -}} - - - - - {{t 'resources.user.title_plural'}} - - - - {{t 'resources.group.title_plural'}} - - - - {{t 'resources.role.title_plural'}} - - - - {{t 'resources.auth-method.title_plural'}} - - \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/aliases/alias/index.hbs b/ui/admin/app/templates/scopes/scope/aliases/alias/index.hbs index bd97059fe..fca455cd6 100644 --- a/ui/admin/app/templates/scopes/scope/aliases/alias/index.hbs +++ b/ui/admin/app/templates/scopes/scope/aliases/alias/index.hbs @@ -33,7 +33,7 @@ {{#if (or perms.canUpdate perms.canDelete)}} - + {{#if (and perms.canUpdate @model.destination_id)}} <:header> -

{{t 'actions.authenticate'}}

+ + {{t + 'actions.authenticate' + }} <:body> -

{{t + {{t 'auth.descriptions.authenticating-with-org' name=@model.scope.displayName - }}

+ }} {{#if (or perms.canUpdate perms.canDelete)}} - +