mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	Sidebar Navigation (#19296)
* Add Helios Design System Components (#19278) * adds hds dependency * updates reset import path * sets minifyCSS advanced option to false * Remove node-sass (#19376) * removes node-sass and fixes sass compilation * fixes active tab li class * Sidebar Navigation Components (#19446) * links ember-shared-components addon and imports styles * adds sidebar frame and nav components * updates HcNav component name to HcAppFrame and adds sidebar UserMenu component * adds tests for sidebar components * fixes tests * updates user menu styling * fixes typos in nav cluster component * changes padding value in sidebar stylesheet to use variable * Replace and remove old nav components with new ones (#19447) * links ember-shared-components addon and imports styles * adds sidebar frame and nav components * updates activeCluster on auth service and adds activeSession prop for sidebar visibility * replaces old nav components with new ones in templates * fixes sidebar visibility issue and updates user menu label class * removes NavHeader usage * adds clients index route to redirect to dashboard * removes unused HcAppFrame footer block and reduces page header top margin * Nav component cleanup (#19681) * removes nav-header components * removes navbar styling * removes status-menu component and styles * removes cluster and auth info components * removes menu-sidebar component and styling * fixes tests * Console Panel Updates (#19741) * updates console panel styling * adds test for opening and closing the console panel * updates console panel background color to use hds token * adds right margin to console panel input * updates link-status banner styling * updates hc nav components to new API * Namespace Picker Updates (#19753) * updates namespace-picker * updates namespace picker menu styling * adds bottom margin to env banner * updates class order on namespace picker link * restores manage namespaces refresh icon * removes manage namespaces nav icon * removes home link component (#20027) * Auth and Error View Updates (#19749) * adds vault logo to auth page * updates top level error template * updates loading substate handling and moves policies link from access to cluster nav (#20033) * moves console panel to bottom of viewport (#20183) * HDS Sidebar Nav Components (#20197) * updates nav components to hds * upgrades project yarn version to 3.5 * fixes issues in app frame component * updates sidenav actions to use icon button component * Sidebar navigation acceptance tests (#20270) * adds sidebar navigation acceptance tests and fixes other test failures * console panel styling tweaks * bumps addon version * remove and ignore yarn install-state file * fixes auth service and console tests * moves classes from deleted files after bulma merge * fixes sass syntax errors blocking build * cleans up dart sass deprecation warnings * adds changelog entry * hides namespace picker when sidebar nav panel is minimized * style tweaks * fixes sidebar nav tests * bumps hds addon to latest version and removes style override * updates modify-passthrough-response helper * updates sidebar nav tests * mfa-setup test fix attempt * fixes cluster mfa setup test * remove deprecated yarn ignore-optional flag from makefile * removes another instance of yarn ignore-optional and updates ui readme * removes unsupported yarn verbose flag from ci-helper * hides nav headings when user does not have access to any sub links * removes unused optional deps and moves lint-staged to dev deps * updates has-permission helper and permissions service tests * fixes issue with console panel not filling container width
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -179,7 +179,7 @@ static-assets-dir: | |||||||
|  |  | ||||||
| install-ui-dependencies: | install-ui-dependencies: | ||||||
| 	@echo "--> Installing JavaScript assets" | 	@echo "--> Installing JavaScript assets" | ||||||
| 	@cd ui && yarn --ignore-optional | 	@cd ui && yarn | ||||||
|  |  | ||||||
| test-ember: install-ui-dependencies | test-ember: install-ui-dependencies | ||||||
| 	@echo "--> Running ember tests" | 	@echo "--> Running ember tests" | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								changelog/19296.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/19296.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:feature | ||||||
|  | **Sidebar Navigation in UI**: A new sidebar navigation panel has been added in the UI to replace the top navigation bar. | ||||||
|  | ``` | ||||||
| @@ -132,9 +132,9 @@ function build_ui() { | |||||||
|   mkdir -p http/web_ui |   mkdir -p http/web_ui | ||||||
|   popd |   popd | ||||||
|   pushd "$repo_root/ui" |   pushd "$repo_root/ui" | ||||||
|   yarn install --ignore-optional |   yarn install | ||||||
|   npm rebuild node-sass |   npm rebuild node-sass | ||||||
|   yarn --verbose run build |   yarn run build | ||||||
|   popd |   popd | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								ui/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								ui/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -29,3 +29,12 @@ package-lock.json | |||||||
|  |  | ||||||
| # broccoli-debug | # broccoli-debug | ||||||
| /DEBUG/ | /DEBUG/ | ||||||
|  |  | ||||||
|  | # yarn | ||||||
|  | .pnp.* | ||||||
|  | .yarn/* | ||||||
|  | !.yarn/patches | ||||||
|  | !.yarn/plugins | ||||||
|  | !.yarn/releases | ||||||
|  | !.yarn/sdks | ||||||
|  | !.yarn/versions | ||||||
|   | |||||||
							
								
								
									
										147221
									
								
								ui/.yarn/releases/yarn-1.19.1.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										147221
									
								
								ui/.yarn/releases/yarn-1.19.1.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										175350
									
								
								ui/.yarn/releases/yarn-1.22.19.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										175350
									
								
								ui/.yarn/releases/yarn-1.22.19.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										873
									
								
								ui/.yarn/releases/yarn-3.5.0.cjs
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										873
									
								
								ui/.yarn/releases/yarn-3.5.0.cjs
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										3
									
								
								ui/.yarnrc.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								ui/.yarnrc.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | nodeLinker: node-modules | ||||||
|  |  | ||||||
|  | yarnPath: .yarn/releases/yarn-3.5.0.cjs | ||||||
| @@ -39,12 +39,6 @@ You will need the following things properly installed on your computer. | |||||||
| * [Yarn](https://yarnpkg.com/) | * [Yarn](https://yarnpkg.com/) | ||||||
| * [Ember CLI](https://cli.emberjs.com/release/) | * [Ember CLI](https://cli.emberjs.com/release/) | ||||||
| * [Google Chrome](https://google.com/chrome/) | * [Google Chrome](https://google.com/chrome/) | ||||||
| - [lint-staged\*](https://www.npmjs.com/package/lint-staged) |  | ||||||
|  |  | ||||||
| \* lint-staged is an optional dependency - running `yarn` will install it. |  | ||||||
| If don't want optional dependencies installed you can run `yarn --ignore-optional`. If you've ignored the optional deps |  | ||||||
| previously and want to install them, you have to tell yarn to refetch all deps by |  | ||||||
| running `yarn --force`. |  | ||||||
|  |  | ||||||
| In order to enforce the same version of `yarn` across installs, the `yarn` binary is included in the repo | In order to enforce the same version of `yarn` across installs, the `yarn` binary is included in the repo | ||||||
| in the `.yarn/releases` folder. To update to a different version of `yarn`, use the `yarn policies set-version VERSION` command. For more information on this, see the [documentation](https://yarnpkg.com/en/docs/cli/policies). | in the `.yarn/releases` folder. To update to a different version of `yarn`, use the `yarn policies set-version VERSION` command. For more information on this, see the [documentation](https://yarnpkg.com/en/docs/cli/policies). | ||||||
|   | |||||||
| @@ -42,7 +42,7 @@ export default ApplicationAdapter.extend({ | |||||||
|       } |       } | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       // no path means this was an error on listing |       // no path means this was an error on listing | ||||||
|       if (!query.path) { |       if (!query.path || !mountModel) { | ||||||
|         throw error; |         throw error; | ||||||
|       } |       } | ||||||
|       // control groups will throw a 403 permission denied error. If this happens return the mountModel |       // control groups will throw a 403 permission denied error. If this happens return the mountModel | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ export default ApplicationAdapter.extend({ | |||||||
|   // concerns and we only want to send "list" to the server |   // concerns and we only want to send "list" to the server | ||||||
|   query(store, type, query) { |   query(store, type, query) { | ||||||
|     let { backend, id } = query; |     let { backend, id } = query; | ||||||
|     return this.ajax(this._url(backend, id), 'GET', { data: { list: true } }).then(resp => { |     return this.ajax(this._url(backend, id), 'GET', { data: { list: true } }).then((resp) => { | ||||||
|       resp.id = id; |       resp.id = id; | ||||||
|       resp.backend = backend; |       resp.backend = backend; | ||||||
|       return resp; |       return resp; | ||||||
| @@ -36,7 +36,7 @@ export default ApplicationAdapter.extend({ | |||||||
|  |  | ||||||
|   queryRecord(store, type, query) { |   queryRecord(store, type, query) { | ||||||
|     let { backend, id } = query; |     let { backend, id } = query; | ||||||
|     return this.ajax(this._url(backend, id), 'GET').then(resp => { |     return this.ajax(this._url(backend, id), 'GET').then((resp) => { | ||||||
|       resp.id = id; |       resp.id = id; | ||||||
|       resp.backend = backend; |       resp.backend = backend; | ||||||
|       return resp; |       return resp; | ||||||
|   | |||||||
| @@ -1,32 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import { inject as service } from '@ember/service'; |  | ||||||
| import Component from '@glimmer/component'; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @module ClusterInfo |  | ||||||
|  * |  | ||||||
|  * @example |  | ||||||
|  * ```js |  | ||||||
|  * <ClusterInfo @cluster={{cluster}} @onLinkClick={{action}} /> |  | ||||||
|  * ``` |  | ||||||
|  * |  | ||||||
|  * @param {object} cluster - details of the current cluster, passed from the parent. |  | ||||||
|  * @param {Function} onLinkClick - parent action which determines the behavior on link click |  | ||||||
|  */ |  | ||||||
| export default class ClusterInfoComponent extends Component { |  | ||||||
|   @service auth; |  | ||||||
|   @service store; |  | ||||||
|   @service version; |  | ||||||
|  |  | ||||||
|   get activeCluster() { |  | ||||||
|     return this.store.peekRecord('cluster', this.auth.activeCluster); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   transitionToRoute() { |  | ||||||
|     this.router.transitionTo(...arguments); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import Component from '@glimmer/component'; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @module HomeLink |  | ||||||
|  * `HomeLink` is a span that contains either the text `home` or the `LogoEdition` component. |  | ||||||
|  * |  | ||||||
|  * @example |  | ||||||
|  * ```js |  | ||||||
|  * <HomeLink @class="navbar-item splash-page-logo"> |  | ||||||
|  *  <LogoEdition /> |  | ||||||
|  * </HomeLink> |  | ||||||
|  * ``` |  | ||||||
|  * @param {string} class - Classes attached to the the component. |  | ||||||
|  * @param {string} text - Text displayed instead of logo. |  | ||||||
|  * |  | ||||||
|  * @see {@link https://github.com/hashicorp/vault/search?l=Handlebars&q=HomeLink|Uses of HomeLink} |  | ||||||
|  * @see {@link https://github.com/hashicorp/vault/blob/main/ui/app/components/home-link.js|HomeLink Source Code} |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| export default class HomeLink extends Component { |  | ||||||
|   get text() { |  | ||||||
|     return 'home'; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   get computedClasses() { |  | ||||||
|     return this.classNames.join(' '); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import Component from '@ember/component'; |  | ||||||
|  |  | ||||||
| export default Component.extend({ |  | ||||||
|   classNames: ['column', 'is-sidebar'], |  | ||||||
|   classNameBindings: ['isActive:is-active'], |  | ||||||
|   isActive: false, |  | ||||||
|   actions: { |  | ||||||
|     openMenu() { |  | ||||||
|       this.set('isActive', true); |  | ||||||
|     }, |  | ||||||
|     closeMenu() { |  | ||||||
|       this.set('isActive', false); |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
| @@ -155,7 +155,9 @@ export default Component.extend({ | |||||||
|  |  | ||||||
|   namespaceDisplay: computed('namespacePath', 'accessibleNamespaces', 'accessibleNamespaces.[]', function () { |   namespaceDisplay: computed('namespacePath', 'accessibleNamespaces', 'accessibleNamespaces.[]', function () { | ||||||
|     const namespace = this.namespacePath; |     const namespace = this.namespacePath; | ||||||
|     if (!namespace) return ''; |     if (!namespace) { | ||||||
|  |       return 'root'; | ||||||
|  |     } | ||||||
|     const parts = namespace?.split('/'); |     const parts = namespace?.split('/'); | ||||||
|     return parts[parts.length - 1]; |     return parts[parts.length - 1]; | ||||||
|   }), |   }), | ||||||
|   | |||||||
| @@ -1,34 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import Component from '@ember/component'; |  | ||||||
| import { inject as service } from '@ember/service'; |  | ||||||
| import { computed } from '@ember/object'; |  | ||||||
|  |  | ||||||
| export default Component.extend({ |  | ||||||
|   router: service(), |  | ||||||
|   currentCluster: service(), |  | ||||||
|   'data-test-navheader': true, |  | ||||||
|   attributeBindings: ['data-test-navheader'], |  | ||||||
|   classNameBindings: 'consoleFullscreen:panel-fullscreen', |  | ||||||
|   tagName: 'header', |  | ||||||
|   navDrawerOpen: false, |  | ||||||
|   consoleFullscreen: false, |  | ||||||
|   hideLinks: computed('router.currentRouteName', function () { |  | ||||||
|     const currentRoute = this.router.currentRouteName; |  | ||||||
|     if ('vault.cluster.oidc-provider' === currentRoute) { |  | ||||||
|       return true; |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
|   }), |  | ||||||
|   actions: { |  | ||||||
|     toggleNavDrawer(isOpen) { |  | ||||||
|       if (isOpen !== undefined) { |  | ||||||
|         return this.set('navDrawerOpen', isOpen); |  | ||||||
|       } |  | ||||||
|       this.toggleProperty('navDrawerOpen'); |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import Component from '@ember/component'; |  | ||||||
|  |  | ||||||
| export default Component.extend({ |  | ||||||
|   tagName: '', |  | ||||||
| }); |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import Component from '@ember/component'; |  | ||||||
|  |  | ||||||
| export default Component.extend({ |  | ||||||
|   tagName: '', |  | ||||||
| }); |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import Component from '@ember/component'; |  | ||||||
|  |  | ||||||
| export default Component.extend({ |  | ||||||
|   tagName: '', |  | ||||||
| }); |  | ||||||
							
								
								
									
										51
									
								
								ui/app/components/sidebar/frame.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								ui/app/components/sidebar/frame.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | <Hds::AppFrame @hasSidebar={{@showSidebar}} @hasHeader={{false}} @hasFooter={{false}} as |Frame|> | ||||||
|  |   <Frame.Sidebar data-test-sidebar-nav> | ||||||
|  |     <Hds::SideNav @isResponsive={{true}} @hasA11yRefocus={{true}} @a11yRefocusSkipTo="app-main-content"> | ||||||
|  |       <:header> | ||||||
|  |         <Hds::SideNav::Header> | ||||||
|  |           <:logo> | ||||||
|  |             <Hds::SideNav::Header::HomeLink | ||||||
|  |               @icon="vault" | ||||||
|  |               @route="vault.cluster" | ||||||
|  |               @model={{this.currentCluster.cluster.name}} | ||||||
|  |               @ariaLabel="home link" | ||||||
|  |               data-test-sidebar-logo | ||||||
|  |             /> | ||||||
|  |           </:logo> | ||||||
|  |           <:actions> | ||||||
|  |             <Hds::SideNav::Header::IconButton | ||||||
|  |               @icon="terminal-screen" | ||||||
|  |               @ariaLabel="Console toggle" | ||||||
|  |               data-test-console-toggle | ||||||
|  |               {{on "click" (fn (mut this.console.isOpen) (not this.console.isOpen))}} | ||||||
|  |             /> | ||||||
|  |             <Sidebar::UserMenu /> | ||||||
|  |           </:actions> | ||||||
|  |         </Hds::SideNav::Header> | ||||||
|  |       </:header> | ||||||
|  |  | ||||||
|  |       {{! this block is where the Hds::SideNav::Portal components render into }} | ||||||
|  |       <:body> | ||||||
|  |         <Hds::SideNav::Portal::Target aria-label="sidebar navigation links" /> | ||||||
|  |       </:body> | ||||||
|  |  | ||||||
|  |       <:footer> | ||||||
|  |         {{#if (has-feature "Namespaces")}} | ||||||
|  |           <NamespacePicker | ||||||
|  |             @namespace={{this.clusterController.namespaceQueryParam}} | ||||||
|  |             class="hds-side-nav-hide-when-minimized" | ||||||
|  |           /> | ||||||
|  |         {{/if}} | ||||||
|  |       </:footer> | ||||||
|  |     </Hds::SideNav> | ||||||
|  |   </Frame.Sidebar> | ||||||
|  |   <Frame.Main id="app-main-content"> | ||||||
|  |     {{! outlet for app content }} | ||||||
|  |     <div id="modal-wormhole"></div> | ||||||
|  |     <LinkStatus @status={{this.currentCluster.cluster.hcpLinkStatus}} /> | ||||||
|  |     {{yield}} | ||||||
|  |     <div data-test-console-panel class={{if this.console.isOpen "panel-open"}}> | ||||||
|  |       <Console::UiPanel @isFullscreen={{this.consoleFullscreen}} /> | ||||||
|  |     </div> | ||||||
|  |   </Frame.Main> | ||||||
|  | </Hds::AppFrame> | ||||||
							
								
								
									
										9
									
								
								ui/app/components/sidebar/frame.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ui/app/components/sidebar/frame.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | import Component from '@glimmer/component'; | ||||||
|  | import { inject as service } from '@ember/service'; | ||||||
|  | import { inject as controller } from '@ember/controller'; | ||||||
|  |  | ||||||
|  | export default class SidebarNavComponent extends Component { | ||||||
|  |   @service currentCluster; | ||||||
|  |   @service console; | ||||||
|  |   @controller('vault.cluster') clusterController; | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								ui/app/components/sidebar/nav/access.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								ui/app/components/sidebar/nav/access.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | <Hds::SideNav::Portal @ariaLabel="Access Navigation Links" data-test-sidebar-nav-panel="Access" as |Nav|> | ||||||
|  |   <Nav.BackLink | ||||||
|  |     @route="vault.cluster" | ||||||
|  |     @current-when={{false}} | ||||||
|  |     @icon="arrow-left" | ||||||
|  |     @text="Back to main navigation" | ||||||
|  |     data-test-sidebar-nav-link="Back to main navigation" | ||||||
|  |   /> | ||||||
|  |  | ||||||
|  |   {{#if (has-permission "access" routeParams=(array "methods" "mfa" "oidc"))}} | ||||||
|  |     <Nav.Title data-test-sidebar-nav-heading="Authentication">Authentication</Nav.Title> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (has-permission "access" routeParams="methods")}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.access.methods" | ||||||
|  |       @current-when="vault.cluster.access.methods vault.cluster.access.method" | ||||||
|  |       @text="Authentication methods" | ||||||
|  |       data-test-sidebar-nav-link="Authentication methods" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (has-permission "access" routeParams="mfa")}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.access.mfa.methods" | ||||||
|  |       @current-when="vault.cluster.access.mfa.methods vault.cluster.access.mfa.enforcements vault.cluster.access.mfa.index" | ||||||
|  |       @text="Multi-factor authentication" | ||||||
|  |       data-test-sidebar-nav-link="Multi-factor authentication" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (has-permission "access" routeParams="oidc")}} | ||||||
|  |     <Nav.Link @route="vault.cluster.access.oidc" @text="OIDC" data-test-sidebar-nav-link="OIDC" /> | ||||||
|  |   {{/if}} | ||||||
|  |  | ||||||
|  |   {{#if (and (has-feature "Control Groups") (has-permission "access" routeParams="control-groups"))}} | ||||||
|  |     <Nav.Title data-test-sidebar-nav-heading="Access Control">Access Control</Nav.Title> | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.access.control-groups" | ||||||
|  |       @current-when="vault.cluster.access.control-groups vault.cluster.access.control-group-accessor vault.cluster.access.control-groups-configure" | ||||||
|  |       @text="Control Groups" | ||||||
|  |       data-test-sidebar-nav-link="Control Groups" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |  | ||||||
|  |   {{#if (has-permission "access" routeParams=(array "namespaces" "groups" "entities"))}} | ||||||
|  |     <Nav.Title data-test-sidebar-nav-heading="Organization">Organization</Nav.Title> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (and (has-feature "Namespaces") (has-permission "access" routeParams="namespaces"))}} | ||||||
|  |     <Nav.Link @route="vault.cluster.access.namespaces" @text="Namespaces" data-test-sidebar-nav-link="Namespaces" /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (has-permission "access" routeParams="groups")}} | ||||||
|  |     <Nav.Link @route="vault.cluster.access.identity" @model="groups" @text="Groups" data-test-sidebar-nav-link="Groups" /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (has-permission "access" routeParams="entities")}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.access.identity" | ||||||
|  |       @model="entities" | ||||||
|  |       @text="Entities" | ||||||
|  |       data-test-sidebar-nav-link="Entities" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |  | ||||||
|  |   {{#if (has-permission "access" routeParams="leases")}} | ||||||
|  |     <Nav.Title data-test-sidebar-nav-heading="Administration">Administration</Nav.Title> | ||||||
|  |     <Nav.Link @route="vault.cluster.access.leases" @text="Leases" data-test-sidebar-nav-link="Leases" /> | ||||||
|  |   {{/if}} | ||||||
|  | </Hds::SideNav::Portal> | ||||||
							
								
								
									
										98
									
								
								ui/app/components/sidebar/nav/cluster.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								ui/app/components/sidebar/nav/cluster.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | |||||||
|  | <Hds::SideNav::Portal @ariaLabel="Cluster Navigation Links" data-test-sidebar-nav-panel="Cluster" as |Nav|> | ||||||
|  |   <Nav.Title data-test-sidebar-nav-heading="Vault">Vault</Nav.Title> | ||||||
|  |  | ||||||
|  |   <Nav.Link | ||||||
|  |     @route="vault.cluster.secrets" | ||||||
|  |     @current-when="vault.cluster.secrets vault.cluster.settings.mount-secret-backend vault.cluster.settings.configure-secret-backend" | ||||||
|  |     @text="Secrets engines" | ||||||
|  |     data-test-sidebar-nav-link="Secrets engines" | ||||||
|  |   /> | ||||||
|  |   {{#if (has-permission "access")}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route={{get (route-params-for "access") "route"}} | ||||||
|  |       @models={{get (route-params-for "access") "models"}} | ||||||
|  |       @current-when="vault.cluster.access vault.cluster.settings.auth" | ||||||
|  |       @text="Access" | ||||||
|  |       @hasSubItems={{true}} | ||||||
|  |       data-test-sidebar-nav-link="Access" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (has-permission "policies")}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.policies" | ||||||
|  |       @models={{get (route-params-for "policies") "models"}} | ||||||
|  |       @text="Policies" | ||||||
|  |       @hasSubItems={{true}} | ||||||
|  |       data-test-sidebar-nav-link="Policies" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (has-permission "tools")}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.tools.tool" | ||||||
|  |       @models={{get (route-params-for "tools") "models"}} | ||||||
|  |       @text="Tools" | ||||||
|  |       @hasSubItems={{true}} | ||||||
|  |       data-test-sidebar-nav-link="Tools" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |  | ||||||
|  |   {{#if | ||||||
|  |     (and this.version.isEnterprise this.cluster.anyReplicationEnabled (has-permission "status" routeParams="replication")) | ||||||
|  |   }} | ||||||
|  |     <Nav.Title data-test-sidebar-nav-heading="Replication">Replication</Nav.Title> | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.replication.mode.index" | ||||||
|  |       @model="dr" | ||||||
|  |       @text="DR Primary" | ||||||
|  |       data-test-sidebar-nav-link="DR Primary" | ||||||
|  |     /> | ||||||
|  |  | ||||||
|  |     {{#if (has-feature "Performance Replication")}} | ||||||
|  |       <Nav.Link | ||||||
|  |         @route="vault.cluster.replication.mode.index" | ||||||
|  |         @model="performance" | ||||||
|  |         @text="Performance Secondary" | ||||||
|  |         data-test-sidebar-nav-link="Performance Secondary" | ||||||
|  |       /> | ||||||
|  |     {{/if}} | ||||||
|  |   {{/if}} | ||||||
|  |  | ||||||
|  |   {{#if | ||||||
|  |     (or | ||||||
|  |       (has-permission "status" routeParams=(array "replication" "raft" "license" "seal")) | ||||||
|  |       (has-permission "clients" routeParams="activity") | ||||||
|  |     ) | ||||||
|  |   }} | ||||||
|  |     <Nav.Title data-test-sidebar-nav-heading="Monitoring">Monitoring</Nav.Title> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (and this.version.isEnterprise (has-permission "status" routeParams="replication"))}} | ||||||
|  |     <Nav.Link @route="vault.cluster.replication.index" @text="Replication" data-test-sidebar-nav-link="Replication" /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (and this.cluster.usingRaft (has-permission "status" routeParams="raft"))}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.storage" | ||||||
|  |       @model={{this.cluster.name}} | ||||||
|  |       @text="Raft Storage" | ||||||
|  |       data-test-sidebar-nav-link="Raft Storage" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (and (has-permission "clients" routeParams="activity") (not this.cluster.dr.isSecondary))}} | ||||||
|  |     <Nav.Link @route="vault.cluster.clients" @text="Client count" data-test-sidebar-nav-link="Client count" /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (and this.version.features (has-permission "status" routeParams="license") (not this.cluster.dr.isSecondary))}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.license" | ||||||
|  |       @model={{this.cluster.name}} | ||||||
|  |       @text="License" | ||||||
|  |       data-test-sidebar-nav-link="License" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (and (has-permission "status" routeParams="seal") (not this.cluster.dr.isSecondary))}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.settings.seal" | ||||||
|  |       @model={{this.cluster.name}} | ||||||
|  |       @text="Seal Vault" | ||||||
|  |       data-test-sidebar-nav-link="Seal Vault" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  | </Hds::SideNav::Portal> | ||||||
							
								
								
									
										12
									
								
								ui/app/components/sidebar/nav/cluster.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								ui/app/components/sidebar/nav/cluster.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | import Component from '@glimmer/component'; | ||||||
|  | import { inject as service } from '@ember/service'; | ||||||
|  |  | ||||||
|  | export default class SidebarNavClusterComponent extends Component { | ||||||
|  |   @service currentCluster; | ||||||
|  |   @service version; | ||||||
|  |   @service auth; | ||||||
|  |  | ||||||
|  |   get cluster() { | ||||||
|  |     return this.currentCluster.cluster; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								ui/app/components/sidebar/nav/policies.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								ui/app/components/sidebar/nav/policies.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | <Hds::SideNav::Portal @ariaLabel="Policies Navigation Links" data-test-sidebar-nav-panel="Policies" as |Nav|> | ||||||
|  |   <Nav.BackLink | ||||||
|  |     @route="vault.cluster" | ||||||
|  |     @current-when={{false}} | ||||||
|  |     @icon="arrow-left" | ||||||
|  |     @text="Back to main navigation" | ||||||
|  |     data-test-sidebar-nav-link="Back to main navigation" | ||||||
|  |   /> | ||||||
|  |  | ||||||
|  |   <Nav.Title data-test-sidebar-nav-heading="Policies">Policies</Nav.Title> | ||||||
|  |  | ||||||
|  |   {{#if (has-permission "policies" routeParams="acl")}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.policies" | ||||||
|  |       @model="acl" | ||||||
|  |       @current-when="vault.cluster.policies vault.cluster.policy" | ||||||
|  |       @text="ACL Policies" | ||||||
|  |       data-test-sidebar-nav-link="ACL Policies" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (and (has-feature "Sentinel") (has-permission "policies" routeParams="rgp"))}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.policies" | ||||||
|  |       @model="rgp" | ||||||
|  |       @current-when="vault.cluster.policies vault.cluster.policy" | ||||||
|  |       @text="Role-Governing Policies" | ||||||
|  |       data-test-sidebar-nav-link="Role-Governing Policies" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  |   {{#if (and (has-feature "Sentinel") (has-permission "policies" routeParams="egp"))}} | ||||||
|  |     <Nav.Link | ||||||
|  |       @route="vault.cluster.policies" | ||||||
|  |       @model="egp" | ||||||
|  |       @current-when="vault.cluster.policies vault.cluster.policy" | ||||||
|  |       @text="Endpoint Governing Policies" | ||||||
|  |       data-test-sidebar-nav-link="Endpoint Governing Policies" | ||||||
|  |     /> | ||||||
|  |   {{/if}} | ||||||
|  | </Hds::SideNav::Portal> | ||||||
							
								
								
									
										22
									
								
								ui/app/components/sidebar/nav/tools.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								ui/app/components/sidebar/nav/tools.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | <Hds::SideNav::Portal @ariaLabel="Tools Navigation Links" data-test-sidebar-nav-panel="Tools" as |Nav|> | ||||||
|  |   <Nav.BackLink | ||||||
|  |     @route="vault.cluster" | ||||||
|  |     @current-when={{false}} | ||||||
|  |     @icon="arrow-left" | ||||||
|  |     @text="Back to main navigation" | ||||||
|  |     data-test-sidebar-nav-link="Back to main navigation" | ||||||
|  |   /> | ||||||
|  |  | ||||||
|  |   <Nav.Title data-test-sidebar-nav-heading="Tools">Tools</Nav.Title> | ||||||
|  |  | ||||||
|  |   {{#each (tools-actions) as |supportedAction|}} | ||||||
|  |     {{#if (has-permission "tools" routeParams=supportedAction)}} | ||||||
|  |       <Nav.Link | ||||||
|  |         @route="vault.cluster.tools.tool" | ||||||
|  |         @model={{supportedAction}} | ||||||
|  |         @text={{capitalize supportedAction}} | ||||||
|  |         data-test-sidebar-nav-link={{capitalize supportedAction}} | ||||||
|  |       /> | ||||||
|  |     {{/if}} | ||||||
|  |   {{/each}} | ||||||
|  | </Hds::SideNav::Portal> | ||||||
							
								
								
									
										83
									
								
								ui/app/components/sidebar/user-menu.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								ui/app/components/sidebar/user-menu.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | <BasicDropdown | ||||||
|  |   @horizontalPosition="right" | ||||||
|  |   @verticalPosition="below" | ||||||
|  |   @renderInPlace={{true}} | ||||||
|  |   class="sidebar-user-menu" | ||||||
|  |   data-test-user-menu | ||||||
|  |   as |Dropdown| | ||||||
|  | > | ||||||
|  |   <Dropdown.Trigger data-test-user-menu-trigger> | ||||||
|  |     <Hds::SideNav::Header::IconButton @icon="user" @ariaLabel="User menu" /> | ||||||
|  |   </Dropdown.Trigger> | ||||||
|  |   <Dropdown.Content> | ||||||
|  |     <Confirm as |c|> | ||||||
|  |       <div class="popup-menu-content" data-test-user-menu-content> | ||||||
|  |         <div class="box"> | ||||||
|  |           <div class="menu-label"> | ||||||
|  |             {{capitalize this.auth.authData.displayName}} | ||||||
|  |           </div> | ||||||
|  |           <nav class="menu"> | ||||||
|  |             <ul class="menu-list"> | ||||||
|  |               {{#if this.auth.allowExpiration}} | ||||||
|  |                 <li class="token-alert is-flex" data-test-user-menu-item="token alert"> | ||||||
|  |                   <span><Icon @name="alert-triangle-fill" class="has-text-highlight" /></span> | ||||||
|  |                   <span class="is-size-8 has-text-semibold"> | ||||||
|  |                     We've stopped auto-renewing your token due to inactivity. It will expire on | ||||||
|  |                     {{date-format this.auth.tokenExpirationDate "MMMM do yyyy, h:mm:ss a"}}. | ||||||
|  |                   </span> | ||||||
|  |                 </li> | ||||||
|  |               {{/if}} | ||||||
|  |               {{#if this.hasEntityId}} | ||||||
|  |                 <li class="action"> | ||||||
|  |                   <LinkTo @route="vault.cluster.mfa-setup" data-test-user-menu-item="mfa"> | ||||||
|  |                     Multi-factor authentication | ||||||
|  |                   </LinkTo> | ||||||
|  |                 </li> | ||||||
|  |               {{/if}} | ||||||
|  |               <li class="action"> | ||||||
|  |                 <CopyButton | ||||||
|  |                   @clipboardText={{this.auth.currentToken}} | ||||||
|  |                   class="link" | ||||||
|  |                   @buttonType="button" | ||||||
|  |                   @success={{action (set-flash-message "Token copied!")}} | ||||||
|  |                 > | ||||||
|  |                   Copy token | ||||||
|  |                 </CopyButton> | ||||||
|  |               </li> | ||||||
|  |               {{#if (is-before (now interval=1000) this.auth.tokenExpirationDate)}} | ||||||
|  |                 {{#if this.auth.authData.renewable}} | ||||||
|  |                   <li class="action"> | ||||||
|  |                     <button | ||||||
|  |                       type="button" | ||||||
|  |                       {{on "click" this.renewToken}} | ||||||
|  |                       class="link button {{if this.isRenewing 'is-loading'}}" | ||||||
|  |                       data-test-user-menu-item="renew token" | ||||||
|  |                     > | ||||||
|  |                       Renew token | ||||||
|  |                     </button> | ||||||
|  |                   </li> | ||||||
|  |                 {{/if}} | ||||||
|  |                 <li class="action"> | ||||||
|  |                   <c.Message | ||||||
|  |                     @id={{get this.auth "authData.displayName"}} | ||||||
|  |                     @title={{concat "Revoke " (get this.auth "authData.displayName") "?"}} | ||||||
|  |                     @onConfirm={{action "revokeToken"}} | ||||||
|  |                     @message="You will not be able to log in again with this token." | ||||||
|  |                     @triggerText="Revoke token" | ||||||
|  |                     @confirmButtonText="Revoke" | ||||||
|  |                     data-test-user-menu-item="revoke token" | ||||||
|  |                   /> | ||||||
|  |                 </li> | ||||||
|  |               {{/if}} | ||||||
|  |               <li class="action"> | ||||||
|  |                 <LinkTo @route="vault.cluster.logout" @model={{this.currentCluster.cluster.name}} id="logout"> | ||||||
|  |                   Log out | ||||||
|  |                 </LinkTo> | ||||||
|  |               </li> | ||||||
|  |             </ul> | ||||||
|  |           </nav> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </Confirm> | ||||||
|  |   </Dropdown.Content> | ||||||
|  | </BasicDropdown> | ||||||
| @@ -1,27 +1,12 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| import Component from '@glimmer/component'; | import Component from '@glimmer/component'; | ||||||
| import { inject as service } from '@ember/service'; | import { inject as service } from '@ember/service'; | ||||||
| import { later } from '@ember/runloop'; | import { later } from '@ember/runloop'; | ||||||
| import { action } from '@ember/object'; | import { action } from '@ember/object'; | ||||||
| import { tracked } from '@glimmer/tracking'; | import { tracked } from '@glimmer/tracking'; | ||||||
| 
 | 
 | ||||||
| /** | export default class SidebarUserMenuComponent extends Component { | ||||||
|  * @module AuthInfo |  | ||||||
|  * |  | ||||||
|  * @example |  | ||||||
|  * ```js
 |  | ||||||
|  * <AuthInfo @activeClusterName={{cluster.name}} @onLinkClick={{action "onLinkClick"}} /> |  | ||||||
|  * ``` |  | ||||||
|  * |  | ||||||
|  * @param {string} activeClusterName - name of the current cluster, passed from the parent. |  | ||||||
|  * @param {Function} onLinkClick - parent action which determines the behavior on link click |  | ||||||
|  */ |  | ||||||
| export default class AuthInfoComponent extends Component { |  | ||||||
|   @service auth; |   @service auth; | ||||||
|  |   @service currentCluster; | ||||||
|   @service router; |   @service router; | ||||||
| 
 | 
 | ||||||
|   @tracked fakeRenew = false; |   @tracked fakeRenew = false; | ||||||
| @@ -29,7 +14,7 @@ export default class AuthInfoComponent extends Component { | |||||||
|   get hasEntityId() { |   get hasEntityId() { | ||||||
|     // root users will not have an entity_id because they are not associated with an entity.
 |     // root users will not have an entity_id because they are not associated with an entity.
 | ||||||
|     // in order to use the MFA end user setup they need an entity_id
 |     // in order to use the MFA end user setup they need an entity_id
 | ||||||
|     return !!this.auth.authData.entity_id; |     return !!this.auth.authData?.entity_id; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get isRenewing() { |   get isRenewing() { | ||||||
| @@ -31,8 +31,4 @@ export default class SplashPage extends Component { | |||||||
|     // default is true unless showTruncatedNavBar is defined as false |     // default is true unless showTruncatedNavBar is defined as false | ||||||
|     return this.args.showTruncatedNavBar === false ? false : true; |     return this.args.showTruncatedNavBar === false ? false : true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   get activeCluster() { |  | ||||||
|     return this.store.peekRecord('cluster', this.auth.activeCluster); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,49 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import Component from '@glimmer/component'; |  | ||||||
| import { action } from '@ember/object'; |  | ||||||
| import { inject as service } from '@ember/service'; |  | ||||||
| import { next } from '@ember/runloop'; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @module StatusMenu |  | ||||||
|  * StatusMenu component is the drop down menu on the main navigation. |  | ||||||
|  * |  | ||||||
|  * @example |  | ||||||
|  * ```js |  | ||||||
|  * <StatusMenu @label='user' @onLinkClick={{action Nav.closeDrawer}}/> |  | ||||||
|  * ``` |  | ||||||
|  * @param {string} [ariaLabel] - aria label for the status icon. |  | ||||||
|  * @param {string} [label] - label for the status menu. |  | ||||||
|  * @param {string} [type] - determines where the component is being used. e.g. replication, auth, etc. |  | ||||||
|  * @param {function} [onLinkClick] - function to handle click on the nested links under content. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| export default class StatusMenu extends Component { |  | ||||||
|   @service currentCluster; |  | ||||||
|   @service auth; |  | ||||||
|   @service media; |  | ||||||
|   @service router; |  | ||||||
|  |  | ||||||
|   get type() { |  | ||||||
|     return this.args.type || 'cluster'; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   get glyphName() { |  | ||||||
|     return this.type === 'user' ? 'user' : 'circle-dot'; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @action |  | ||||||
|   onLinkClick(dropdown) { |  | ||||||
|     if (dropdown) { |  | ||||||
|       // strange issue where closing dropdown triggers full transition which redirects to auth screen in production builds |  | ||||||
|       // closing dropdown in next tick of run loop fixes it |  | ||||||
|       next(() => dropdown.actions.close()); |  | ||||||
|     } |  | ||||||
|     this.args.onLinkClick(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -5,19 +5,10 @@ | |||||||
|  |  | ||||||
| import { inject as service } from '@ember/service'; | import { inject as service } from '@ember/service'; | ||||||
| import Controller from '@ember/controller'; | import Controller from '@ember/controller'; | ||||||
| import { computed } from '@ember/object'; |  | ||||||
| import config from '../config/environment'; | import config from '../config/environment'; | ||||||
|  |  | ||||||
| export default Controller.extend({ | export default Controller.extend({ | ||||||
|   env: config.environment, |   env: config.environment, | ||||||
|   auth: service(), |   auth: service(), | ||||||
|   store: service(), |   store: service(), | ||||||
|   activeCluster: computed('auth.activeCluster', function () { |  | ||||||
|     const id = this.auth.activeCluster; |  | ||||||
|     return id ? this.store.peekRecord('cluster', id) : null; |  | ||||||
|   }), |  | ||||||
|   activeClusterName: computed('activeCluster', function () { |  | ||||||
|     const activeCluster = this.activeCluster; |  | ||||||
|     return activeCluster ? activeCluster.get('name') : null; |  | ||||||
|   }), |  | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ | |||||||
|  |  | ||||||
| import { inject as service } from '@ember/service'; | import { inject as service } from '@ember/service'; | ||||||
| import Controller from '@ember/controller'; | import Controller from '@ember/controller'; | ||||||
| import { computed } from '@ember/object'; |  | ||||||
| import config from '../config/environment'; | import config from '../config/environment'; | ||||||
|  |  | ||||||
| export default Controller.extend({ | export default Controller.extend({ | ||||||
| @@ -20,12 +19,4 @@ export default Controller.extend({ | |||||||
|   env: config.environment, |   env: config.environment, | ||||||
|   auth: service(), |   auth: service(), | ||||||
|   store: service(), |   store: service(), | ||||||
|   activeCluster: computed('auth.activeCluster', function () { |  | ||||||
|     const id = this.auth.activeCluster; |  | ||||||
|     return id ? this.store.peekRecord('cluster', id) : null; |  | ||||||
|   }), |  | ||||||
|   activeClusterName: computed('activeCluster', function () { |  | ||||||
|     const activeCluster = this.activeCluster; |  | ||||||
|     return activeCluster ? activeCluster.get('name') : null; |  | ||||||
|   }), |  | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
| import { inject as service } from '@ember/service'; | import { inject as service } from '@ember/service'; | ||||||
| import { alias } from '@ember/object/computed'; | import { alias } from '@ember/object/computed'; | ||||||
| import Controller from '@ember/controller'; | import Controller from '@ember/controller'; | ||||||
| import { observer, computed } from '@ember/object'; | import { observer } from '@ember/object'; | ||||||
| export default Controller.extend({ | export default Controller.extend({ | ||||||
|   auth: service(), |   auth: service(), | ||||||
|   store: service(), |   store: service(), | ||||||
| @@ -36,35 +36,7 @@ export default Controller.extend({ | |||||||
|   }), |   }), | ||||||
|  |  | ||||||
|   consoleOpen: alias('console.isOpen'), |   consoleOpen: alias('console.isOpen'), | ||||||
|  |   activeCluster: alias('auth.activeCluster'), | ||||||
|   activeCluster: computed('auth.activeCluster', function () { |  | ||||||
|     return this.store.peekRecord('cluster', this.auth.activeCluster); |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   activeClusterName: computed('activeCluster', function () { |  | ||||||
|     const activeCluster = this.activeCluster; |  | ||||||
|     return activeCluster ? activeCluster.get('name') : null; |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   showNav: computed( |  | ||||||
|     'router.currentRouteName', |  | ||||||
|     'activeClusterName', |  | ||||||
|     'auth.currentToken', |  | ||||||
|     'activeCluster.{dr.isSecondary,needsInit,sealed}', |  | ||||||
|     function () { |  | ||||||
|       if (this.activeCluster.dr?.isSecondary || this.activeCluster.needsInit || this.activeCluster.sealed) { |  | ||||||
|         return false; |  | ||||||
|       } |  | ||||||
|       if ( |  | ||||||
|         this.activeClusterName && |  | ||||||
|         this.auth.currentToken && |  | ||||||
|         this.router.currentRouteName !== 'vault.cluster.auth' |  | ||||||
|       ) { |  | ||||||
|         return true; |  | ||||||
|       } |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|   ), |  | ||||||
|  |  | ||||||
|   actions: { |   actions: { | ||||||
|     toggleConsole() { |     toggleConsole() { | ||||||
|   | |||||||
| @@ -20,9 +20,9 @@ export default Helper.extend({ | |||||||
|   ), |   ), | ||||||
|  |  | ||||||
|   compute([route], params) { |   compute([route], params) { | ||||||
|     const { routeParams } = params; |     const { routeParams, requireAll } = params; | ||||||
|     const permissions = this.permissions; |     const permissions = this.permissions; | ||||||
|  |  | ||||||
|     return permissions.hasNavPermission(route, routeParams); |     return permissions.hasNavPermission(route, routeParams, requireAll); | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -135,18 +135,5 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, { | |||||||
|       } |       } | ||||||
|       return true; |       return true; | ||||||
|     }, |     }, | ||||||
|     loading(transition) { |  | ||||||
|       const isSameRoute = transition.from?.name === transition.to?.name; |  | ||||||
|       if (isSameRoute || Ember.testing) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       // eslint-disable-next-line ember/no-controller-access-in-routes |  | ||||||
|       const controller = this.controllerFor('vault.cluster'); |  | ||||||
|       controller.set('currentlyLoading', true); |  | ||||||
|  |  | ||||||
|       transition.finally(function () { |  | ||||||
|         controller.set('currentlyLoading', false); |  | ||||||
|       }); |  | ||||||
|     }, |  | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -35,16 +35,6 @@ export default class ClientsRoute extends Route { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @action |  | ||||||
|   async loading(transition) { |  | ||||||
|     // eslint-disable-next-line ember/no-controller-access-in-routes |  | ||||||
|     const controller = this.controllerFor(this.routeName); |  | ||||||
|     controller.set('currentlyLoading', true); |  | ||||||
|     transition.promise.finally(function () { |  | ||||||
|       controller.set('currentlyLoading', false); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @action |   @action | ||||||
|   deactivate() { |   deactivate() { | ||||||
|     // when navigating away from parent route, delete manually inputted license start date |     // when navigating away from parent route, delete manually inputted license start date | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								ui/app/routes/vault/cluster/clients/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								ui/app/routes/vault/cluster/clients/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | import Route from '@ember/routing/route'; | ||||||
|  | import { inject as service } from '@ember/service'; | ||||||
|  |  | ||||||
|  | export default class ClientsIndexRoute extends Route { | ||||||
|  |   @service router; | ||||||
|  |  | ||||||
|  |   redirect() { | ||||||
|  |     this.router.transitionTo('vault.cluster.clients.dashboard'); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -26,20 +26,24 @@ export { TOKEN_SEPARATOR, TOKEN_PREFIX, ROOT_PREFIX }; | |||||||
|  |  | ||||||
| export default Service.extend({ | export default Service.extend({ | ||||||
|   permissions: service(), |   permissions: service(), | ||||||
|  |   store: service(), | ||||||
|  |   router: service(), | ||||||
|   namespaceService: service('namespace'), |   namespaceService: service('namespace'), | ||||||
|  |  | ||||||
|   IDLE_TIMEOUT: 3 * 60e3, |   IDLE_TIMEOUT: 3 * 60e3, | ||||||
|   expirationCalcTS: null, |   expirationCalcTS: null, | ||||||
|   isRenewing: false, |   isRenewing: false, | ||||||
|   mfaErrors: null, |   mfaErrors: null, | ||||||
|  |  | ||||||
|   init() { |   get tokenExpired() { | ||||||
|     this._super(...arguments); |     const expiration = this.tokenExpirationDate; | ||||||
|     this.checkForRootToken(); |     return expiration ? this.now() >= expiration : null; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   clusterAdapter() { |   get activeCluster() { | ||||||
|     return getOwner(this).lookup('adapter:cluster'); |     return this.activeClusterId ? this.store.peekRecord('cluster', this.activeClusterId) : null; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   // eslint-disable-next-line |   // eslint-disable-next-line | ||||||
|   tokens: computed({ |   tokens: computed({ | ||||||
|     get() { |     get() { | ||||||
| @@ -50,6 +54,84 @@ export default Service.extend({ | |||||||
|     }, |     }, | ||||||
|   }), |   }), | ||||||
|  |  | ||||||
|  |   isActiveSession: computed( | ||||||
|  |     'router.currentRouteName', | ||||||
|  |     'currentToken', | ||||||
|  |     'activeCluster.{dr.isSecondary,needsInit,sealed,name}', | ||||||
|  |     function () { | ||||||
|  |       if (this.activeCluster) { | ||||||
|  |         if (this.activeCluster.dr?.isSecondary || this.activeCluster.needsInit || this.activeCluster.sealed) { | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  |         if ( | ||||||
|  |           this.activeCluster.name && | ||||||
|  |           this.currentToken && | ||||||
|  |           this.router.currentRouteName !== 'vault.cluster.auth' | ||||||
|  |         ) { | ||||||
|  |           return true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   ), | ||||||
|  |  | ||||||
|  |   tokenExpirationDate: computed('currentTokenName', 'expirationCalcTS', function () { | ||||||
|  |     const tokenName = this.currentTokenName; | ||||||
|  |     if (!tokenName) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     const { tokenExpirationEpoch } = this.getTokenData(tokenName); | ||||||
|  |     const expirationDate = new Date(0); | ||||||
|  |     return tokenExpirationEpoch ? expirationDate.setUTCMilliseconds(tokenExpirationEpoch) : null; | ||||||
|  |   }), | ||||||
|  |  | ||||||
|  |   renewAfterEpoch: computed('currentTokenName', 'expirationCalcTS', function () { | ||||||
|  |     const tokenName = this.currentTokenName; | ||||||
|  |     const { expirationCalcTS } = this; | ||||||
|  |     const data = this.getTokenData(tokenName); | ||||||
|  |     if (!tokenName || !data || !expirationCalcTS) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |     const { ttl, renewable } = data; | ||||||
|  |     // renew after last expirationCalc time + half of the ttl (in ms) | ||||||
|  |     return renewable ? Math.floor((ttl * 1e3) / 2) + expirationCalcTS : null; | ||||||
|  |   }), | ||||||
|  |  | ||||||
|  |   // returns the key for the token to use | ||||||
|  |   currentTokenName: computed('activeClusterId', 'tokens', 'tokens.[]', function () { | ||||||
|  |     const regex = new RegExp(this.activeClusterId); | ||||||
|  |     return this.tokens.find((key) => regex.test(key)); | ||||||
|  |   }), | ||||||
|  |  | ||||||
|  |   currentToken: computed('currentTokenName', function () { | ||||||
|  |     const name = this.currentTokenName; | ||||||
|  |     const data = name && this.getTokenData(name); | ||||||
|  |     // data.token is undefined so that's why it returns current token undefined | ||||||
|  |     return name && data ? data.token : null; | ||||||
|  |   }), | ||||||
|  |  | ||||||
|  |   authData: computed('currentTokenName', function () { | ||||||
|  |     const token = this.currentTokenName; | ||||||
|  |     if (!token) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     const backend = this.backendFromTokenName(token); | ||||||
|  |     const stored = this.getTokenData(token); | ||||||
|  |  | ||||||
|  |     return assign(stored, { | ||||||
|  |       backend: BACKENDS.findBy('type', backend), | ||||||
|  |     }); | ||||||
|  |   }), | ||||||
|  |  | ||||||
|  |   init() { | ||||||
|  |     this._super(...arguments); | ||||||
|  |     this.checkForRootToken(); | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   clusterAdapter() { | ||||||
|  |     return getOwner(this).lookup('adapter:cluster'); | ||||||
|  |   }, | ||||||
|  |  | ||||||
|   generateTokenName({ backend, clusterId }, policies) { |   generateTokenName({ backend, clusterId }, policies) { | ||||||
|     return (policies || []).includes('root') |     return (policies || []).includes('root') | ||||||
|       ? `${TOKEN_PREFIX}${ROOT_PREFIX}${TOKEN_SEPARATOR}${clusterId}` |       ? `${TOKEN_PREFIX}${ROOT_PREFIX}${TOKEN_SEPARATOR}${clusterId}` | ||||||
| @@ -83,7 +165,7 @@ export default Service.extend({ | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   setCluster(clusterId) { |   setCluster(clusterId) { | ||||||
|     this.set('activeCluster', clusterId); |     this.set('activeClusterId', clusterId); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   ajax(url, method, options) { |   ajax(url, method, options) { | ||||||
| @@ -196,7 +278,7 @@ export default Service.extend({ | |||||||
|     tokenName = this.generateTokenName( |     tokenName = this.generateTokenName( | ||||||
|       { |       { | ||||||
|         backend, |         backend, | ||||||
|         clusterId: (options && options.clusterId) || this.activeCluster, |         clusterId: (options && options.clusterId) || this.activeClusterId, | ||||||
|       }, |       }, | ||||||
|       resp.policies |       resp.policies | ||||||
|     ); |     ); | ||||||
| @@ -231,33 +313,6 @@ export default Service.extend({ | |||||||
|     return this.storage(token).removeItem(token); |     return this.storage(token).removeItem(token); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   tokenExpirationDate: computed('currentTokenName', 'expirationCalcTS', function () { |  | ||||||
|     const tokenName = this.currentTokenName; |  | ||||||
|     if (!tokenName) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     const { tokenExpirationEpoch } = this.getTokenData(tokenName); |  | ||||||
|     const expirationDate = new Date(0); |  | ||||||
|     return tokenExpirationEpoch ? expirationDate.setUTCMilliseconds(tokenExpirationEpoch) : null; |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   get tokenExpired() { |  | ||||||
|     const expiration = this.tokenExpirationDate; |  | ||||||
|     return expiration ? this.now() >= expiration : null; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   renewAfterEpoch: computed('currentTokenName', 'expirationCalcTS', function () { |  | ||||||
|     const tokenName = this.currentTokenName; |  | ||||||
|     const { expirationCalcTS } = this; |  | ||||||
|     const data = this.getTokenData(tokenName); |  | ||||||
|     if (!tokenName || !data || !expirationCalcTS) { |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|     const { ttl, renewable } = data; |  | ||||||
|     // renew after last expirationCalc time + half of the ttl (in ms) |  | ||||||
|     return renewable ? Math.floor((ttl * 1e3) / 2) + expirationCalcTS : null; |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   renew() { |   renew() { | ||||||
|     const tokenName = this.currentTokenName; |     const tokenName = this.currentTokenName; | ||||||
|     const currentlyRenewing = this.isRenewing; |     const currentlyRenewing = this.isRenewing; | ||||||
| @@ -422,32 +477,6 @@ export default Service.extend({ | |||||||
|     this.set('tokens', tokenNames); |     this.set('tokens', tokenNames); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   // returns the key for the token to use |  | ||||||
|   currentTokenName: computed('activeCluster', 'tokens', 'tokens.[]', function () { |  | ||||||
|     const regex = new RegExp(this.activeCluster); |  | ||||||
|     return this.tokens.find((key) => regex.test(key)); |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   currentToken: computed('currentTokenName', function () { |  | ||||||
|     const name = this.currentTokenName; |  | ||||||
|     const data = name && this.getTokenData(name); |  | ||||||
|     // data.token is undefined so that's why it returns current token undefined |  | ||||||
|     return name && data ? data.token : null; |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   authData: computed('currentTokenName', function () { |  | ||||||
|     const token = this.currentTokenName; |  | ||||||
|     if (!token) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     const backend = this.backendFromTokenName(token); |  | ||||||
|     const stored = this.getTokenData(token); |  | ||||||
|  |  | ||||||
|     return assign(stored, { |  | ||||||
|       backend: BACKENDS.findBy('type', backend), |  | ||||||
|     }); |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   getOktaNumberChallengeAnswer(nonce, mount) { |   getOktaNumberChallengeAnswer(nonce, mount) { | ||||||
|     const url = `/v1/auth/${mount}/verify/${nonce}`; |     const url = `/v1/auth/${mount}/verify/${nonce}`; | ||||||
|     return this.ajax(url, 'GET', {}).then( |     return this.ajax(url, 'GET', {}).then( | ||||||
|   | |||||||
| @@ -95,12 +95,15 @@ export default Service.extend({ | |||||||
|     this.set('canViewAll', null); |     this.set('canViewAll', null); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   hasNavPermission(navItem, routeParams) { |   hasNavPermission(navItem, routeParams, requireAll) { | ||||||
|     if (routeParams) { |     if (routeParams) { | ||||||
|       // viewing the entity and groups pages require the list capability, while the others require the default, which is anything other than deny |       // viewing the entity and groups pages require the list capability, while the others require the default, which is anything other than deny | ||||||
|       const capability = routeParams === 'entities' || routeParams === 'groups' ? ['list'] : [null]; |       const capability = routeParams === 'entities' || routeParams === 'groups' ? ['list'] : [null]; | ||||||
|  |       // check that the user has permission to access all (requireAll = true) or any of the routes when array is passed | ||||||
|       return this.hasPermission(API_PATHS[navItem][routeParams], capability); |       // useful for hiding nav headings when user does not have access to any of the links | ||||||
|  |       const params = Array.isArray(routeParams) ? routeParams : [routeParams]; | ||||||
|  |       const evalMethod = !Array.isArray(routeParams) || requireAll ? 'every' : 'some'; | ||||||
|  |       return params[evalMethod]((param) => this.hasPermission(API_PATHS[navItem][param], capability)); | ||||||
|     } |     } | ||||||
|     return Object.values(API_PATHS[navItem]).some((path) => this.hasPermission(path)); |     return Object.values(API_PATHS[navItem]).some((path) => this.hasPermission(path)); | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -3,8 +3,10 @@ | |||||||
|  * SPDX-License-Identifier: MPL-2.0 |  * SPDX-License-Identifier: MPL-2.0 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | @import './reset'; | ||||||
| @import 'ember-basic-dropdown'; | @import 'ember-basic-dropdown'; | ||||||
| @import 'ember-power-select'; | @import 'ember-power-select'; | ||||||
|  | @import '@hashicorp/design-system-components'; | ||||||
| @import './core'; | @import './core'; | ||||||
|  |  | ||||||
| @mixin font-face($name) { | @mixin font-face($name) { | ||||||
|   | |||||||
| @@ -3,25 +3,26 @@ | |||||||
|  * SPDX-License-Identifier: MPL-2.0 |  * SPDX-License-Identifier: MPL-2.0 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | $console-close-height: 35px; | ||||||
|  |  | ||||||
| .console-ui-panel { | .console-ui-panel { | ||||||
|   background: linear-gradient(to right, #191a1c, #1b212d); |   background: var(--token-color-palette-neutral-700); | ||||||
|  |   width: -moz-available; | ||||||
|  |   width: -webkit-fill-available; | ||||||
|   height: 0; |   height: 0; | ||||||
|   left: 0; |  | ||||||
|   position: fixed; |  | ||||||
|   min-height: 0; |   min-height: 0; | ||||||
|   overflow: scroll; |   overflow: auto; | ||||||
|   right: 0; |   position: fixed; | ||||||
|   top: 4rem; |   bottom: 0; | ||||||
|   transition: min-height $speed $easing, transform $speed ease-in; |   transition: min-height $speed $easing, transform $speed ease-in; | ||||||
|   will-change: transform, min-height; |   will-change: transform, min-height; | ||||||
|   -webkit-overflow-scrolling: touch; |   -webkit-overflow-scrolling: touch; | ||||||
|   width: 100vw; |  | ||||||
|   z-index: 199; |   z-index: 199; | ||||||
|  |  | ||||||
|   .button { |   .button { | ||||||
|     background: transparent; |     background: transparent; | ||||||
|     border: none; |     border: none; | ||||||
|     color: $grey; |     color: $white; | ||||||
|     min-width: 0; |     min-width: 0; | ||||||
|     padding: 0 $size-8; |     padding: 0 $size-8; | ||||||
|  |  | ||||||
| @@ -40,8 +41,8 @@ | |||||||
|   font-size: 14px; |   font-size: 14px; | ||||||
|   font-weight: $font-weight-semibold; |   font-weight: $font-weight-semibold; | ||||||
|   justify-content: flex-end; |   justify-content: flex-end; | ||||||
|   min-height: 100%; |   min-height: calc(100% - $console-close-height); // account for close button that is sticky positioned | ||||||
|   padding: $size-8 $size-8 $size-4; |   padding: $size-8 $size-8 $size-5; | ||||||
|   transition: justify-content $speed ease-in; |   transition: justify-content $speed ease-in; | ||||||
|  |  | ||||||
|   pre, |   pre, | ||||||
| @@ -78,16 +79,17 @@ | |||||||
|  |  | ||||||
|   input { |   input { | ||||||
|     background-color: rgba($black, 0.5); |     background-color: rgba($black, 0.5); | ||||||
|     border: 0; |     border: 1px solid var(--token-color-palette-neutral-500); | ||||||
|  |     border-radius: 2px; | ||||||
|     caret-color: $white; |     caret-color: $white; | ||||||
|     color: $white; |     color: $white; | ||||||
|     flex: 1 1 auto; |     flex: 1 1 auto; | ||||||
|     font-family: $family-monospace; |     font-family: $family-monospace; | ||||||
|     font-size: 16px; |     font-size: 16px; | ||||||
|     font-weight: $font-weight-bold; |     font-weight: $font-weight-bold; | ||||||
|     margin-left: -$size-10; |  | ||||||
|     outline: none; |     outline: none; | ||||||
|     padding: $size-10; |     padding: $size-10; | ||||||
|  |     margin-right: $spacing-xs; | ||||||
|     transition: background-color $speed; |     transition: background-color $speed; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -125,31 +127,9 @@ | |||||||
|  |  | ||||||
| .panel-open .console-ui-panel.fullscreen { | .panel-open .console-ui-panel.fullscreen { | ||||||
|   bottom: 0; |   bottom: 0; | ||||||
|   top: 0; |   right: 0; | ||||||
|   min-height: 100vh; |   min-height: 100vh; | ||||||
| } |   width: 100%; | ||||||
|  |  | ||||||
| .panel-open { |  | ||||||
|   .navbar, |  | ||||||
|   .navbar-sections { |  | ||||||
|     transition: transform $speed ease-in; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .panel-open.panel-fullscreen { |  | ||||||
|   .navbar, |  | ||||||
|   .navbar-sections { |  | ||||||
|     @include from($mobile) { |  | ||||||
|       transform: translateY(-100px); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| header .navbar, |  | ||||||
| header .navbar-sections { |  | ||||||
|   z-index: 200; |  | ||||||
|   transform: translateY(0); |  | ||||||
|   will-change: transform; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .console-spinner.control { | .console-spinner.control { | ||||||
| @@ -168,12 +148,14 @@ header .navbar-sections { | |||||||
| } | } | ||||||
|  |  | ||||||
| .console-close-button { | .console-close-button { | ||||||
|   position: absolute; |   position: sticky; | ||||||
|   top: -3.25rem; |   top: $spacing-xs; | ||||||
|   right: $spacing-xs; |   height: $console-close-height; | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: flex-end; | ||||||
|   z-index: 210; |   z-index: 210; | ||||||
|  |  | ||||||
|   @include from($mobile) { |   button { | ||||||
|     display: none; |     margin-right: $spacing-xs; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ | |||||||
|   animation: env-banner-color-rotate 8s infinite linear alternate; |   animation: env-banner-color-rotate 8s infinite linear alternate; | ||||||
|   color: $white; |   color: $white; | ||||||
|   margin-top: -20px; |   margin-top: -20px; | ||||||
|  |   margin-bottom: 6px; | ||||||
|  |  | ||||||
|   .hs-icon { |   .hs-icon { | ||||||
|     margin: 0; |     margin: 0; | ||||||
|   | |||||||
| @@ -74,3 +74,11 @@ | |||||||
|   width: 32px; |   width: 32px; | ||||||
|   height: 32px; |   height: 32px; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .brand-icon-large { | ||||||
|  |   width: 62px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .error-icon { | ||||||
|  |   width: 48px; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -5,65 +5,33 @@ | |||||||
|  |  | ||||||
| .namespace-picker { | .namespace-picker { | ||||||
|   position: relative; |   position: relative; | ||||||
|   color: $white; |   color: var(--token-color-palette-neutral-300); | ||||||
|   display: flex; |   display: flex; | ||||||
|   fill: $white; |  | ||||||
|   padding: $spacing-xxs $spacing-xs; |   padding: $spacing-xxs $spacing-xs; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|  |  | ||||||
|   @include from($mobile) { |  | ||||||
|     margin-left: -$spacing-xs; |  | ||||||
|     padding: $spacing-xxs 0 $spacing-xxs $spacing-s; |  | ||||||
|     width: auto; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .namespace-picker.no-namespaces { | .namespace-picker.no-namespaces { | ||||||
|   border: none; |   border: none; | ||||||
|   padding-right: 0; |   padding-right: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| .namespace-picker-trigger { | .namespace-picker-trigger { | ||||||
|   align-items: center; |   align-items: center; | ||||||
|   display: flex; |   display: flex; | ||||||
|   flex: 1 1 auto; |   flex: 1 1 auto; | ||||||
|   height: 2rem; |   height: 2rem; | ||||||
|   justify-content: space-between; |   justify-content: space-between; | ||||||
|   padding: 0; |   margin-right: $spacing-xxs; | ||||||
|   text-align: left; |  | ||||||
|  |  | ||||||
|   @include from($mobile) { |  | ||||||
|     height: auto; |  | ||||||
|     padding: $spacing-xs $spacing-m; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .is-status-chevron { |  | ||||||
|     transform: rotate(-90deg); |  | ||||||
|  |  | ||||||
|     @include from($mobile) { |  | ||||||
|       transform: rotate(0deg); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   &.ember-basic-dropdown-trigger--below .is-status-chevron { |  | ||||||
|     transform: rotate(0deg); |  | ||||||
|  |  | ||||||
|     @include from($mobile) { |  | ||||||
|       transform: rotate(180deg); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .namespace-name { | .namespace-name { | ||||||
|   display: inline-block; |  | ||||||
|   flex: 1 1 auto; |  | ||||||
|   font-size: 1rem; |   font-size: 1rem; | ||||||
|   margin: 0 $spacing-xs; |   margin-left: $spacing-xs; | ||||||
|  |  | ||||||
|   @include from($mobile) { |  | ||||||
|     margin-left: $size-10; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .namespace-picker-content { | .namespace-picker-content { | ||||||
|   width: $drawer-width - ($spacing-xs * 2); |   width: 250px; | ||||||
|   max-height: 300px; |   max-height: 300px; | ||||||
|   overflow: auto; |   overflow: auto; | ||||||
|   border-radius: $radius; |   border-radius: $radius; | ||||||
| @@ -72,10 +40,6 @@ | |||||||
|   &.ember-basic-dropdown-content { |   &.ember-basic-dropdown-content { | ||||||
|     background: $white; |     background: $white; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @include from($mobile) { |  | ||||||
|     width: $drawer-width; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .namespace-picker-content .level-left { | .namespace-picker-content .level-left { | ||||||
| @@ -95,6 +59,14 @@ | |||||||
|  |  | ||||||
| .namespace-manage-link { | .namespace-manage-link { | ||||||
|   border-top: 1px solid rgba($black, 0.1); |   border-top: 1px solid rgba($black, 0.1); | ||||||
|  |  | ||||||
|  |   .level-left { | ||||||
|  |     font-weight: $font-weight-bold; | ||||||
|  |     font-size: 14px; | ||||||
|  |   } | ||||||
|  |   .level-right { | ||||||
|  |     margin-right: 10px; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .namespace-list { | .namespace-list { | ||||||
| @@ -112,6 +84,11 @@ | |||||||
| .namespace-link.is-current { | .namespace-link.is-current { | ||||||
|   margin-top: $size-8; |   margin-top: $size-8; | ||||||
|   margin-right: -$size-10; |   margin-right: -$size-10; | ||||||
|  |  | ||||||
|  |   svg { | ||||||
|  |     margin-top: 2px; | ||||||
|  |     color: var(--token-color-border-strong); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .leaf-panel { | .leaf-panel { | ||||||
| @@ -127,9 +104,11 @@ | |||||||
|   bottom: 0; |   bottom: 0; | ||||||
|   z-index: 1; |   z-index: 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| .leaf-panel-left { | .leaf-panel-left { | ||||||
|   transform: translateX(-$drawer-width); |   transform: translateX(-$drawer-width); | ||||||
| } | } | ||||||
|  |  | ||||||
| .leaf-panel-adding, | .leaf-panel-adding, | ||||||
| .leaf-panel-current { | .leaf-panel-current { | ||||||
|   position: relative; |   position: relative; | ||||||
| @@ -137,6 +116,7 @@ | |||||||
|     margin-bottom: 4px; |     margin-bottom: 4px; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .animated-list { | .animated-list { | ||||||
|   .leaf-panel-exiting, |   .leaf-panel-exiting, | ||||||
|   .leaf-panel-adding { |   .leaf-panel-adding { | ||||||
| @@ -144,6 +124,7 @@ | |||||||
|     z-index: 20; |     z-index: 20; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .leaf-panel-adding { | .leaf-panel-adding { | ||||||
|   z-index: 100; |   z-index: 100; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   .title { |   .title { | ||||||
|     margin-top: $size-1; |     margin-top: $size-2; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .title-with-icon { |   .title-with-icon { | ||||||
|   | |||||||
| @@ -132,16 +132,6 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .status-menu-content { |  | ||||||
|   margin-top: 8px; |  | ||||||
|  |  | ||||||
|   .box { |  | ||||||
|     @include until($mobile) { |  | ||||||
|       width: $drawer-width - ($spacing-xs * 2); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .ember-basic-dropdown-content { | .ember-basic-dropdown-content { | ||||||
|   background-color: transparent; |   background-color: transparent; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,107 +3,44 @@ | |||||||
|  * SPDX-License-Identifier: MPL-2.0 |  * SPDX-License-Identifier: MPL-2.0 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| .is-sidebar { | .sidebar-user-menu { | ||||||
|   border-right: $base-border; |   align-self: center; | ||||||
|   display: flex; |  | ||||||
|   flex: 1 1 auto; |  | ||||||
|   margin: 0.75rem 0.75rem 0.75rem 0; |  | ||||||
|   padding: 0 0 0 0.75rem; |  | ||||||
|  |  | ||||||
|   @include until($mobile) { |  | ||||||
|     background-color: $white; |  | ||||||
|     bottom: 0; |  | ||||||
|     left: -1.5rem; |  | ||||||
|     margin: 0; |  | ||||||
|     max-width: $drawer-width; |  | ||||||
|     padding: $spacing-m 0 0; |  | ||||||
|     position: absolute; |  | ||||||
|     right: $size-2; |  | ||||||
|     transform: translateX(-100%); |  | ||||||
|     transition: transform $speed; |  | ||||||
|     top: 0; |  | ||||||
|     z-index: 5; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   &.is-active { |  | ||||||
|     @include until($mobile) { |  | ||||||
|       transform: translateX(0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     .menu-toggle { |  | ||||||
|       left: auto; |  | ||||||
|       right: $size-10; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .menu-toggle { |  | ||||||
|     color: $blue; |  | ||||||
|     cursor: pointer; |  | ||||||
|     display: none; |  | ||||||
|     margin-left: $size-10; |  | ||||||
|     left: 100%; |  | ||||||
|     position: absolute; |  | ||||||
|     top: 0; |  | ||||||
|  |  | ||||||
|     @include until($mobile) { |  | ||||||
|       display: block; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     .button { |  | ||||||
|       min-width: 0; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .menu { |  | ||||||
|     flex: 1 1 auto; |  | ||||||
|     padding-top: 5.25rem; |  | ||||||
|     position: relative; |  | ||||||
|  |  | ||||||
|     @include until($mobile) { |  | ||||||
|       padding-top: $size-6; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|  |   .popup-menu-content { | ||||||
|     .menu-label { |     .menu-label { | ||||||
|     color: $grey-light; |       color: $black; | ||||||
|  |       font-size: 14px; | ||||||
|       font-weight: $font-weight-bold; |       font-weight: $font-weight-bold; | ||||||
|     font-size: $size-8; |       text-transform: unset; | ||||||
|     line-height: 1; |  | ||||||
|     margin-bottom: $size-8; |  | ||||||
|     padding-left: $size-5; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .menu-list { |  | ||||||
|     border-top: $base-border; |  | ||||||
|     padding: $size-9 0; |  | ||||||
|  |  | ||||||
|     @include until($mobile) { |  | ||||||
|       padding-top: $size-4; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     li { |  | ||||||
|       a { |  | ||||||
|         &.active { |  | ||||||
|           border-right: 4px solid $blue; |  | ||||||
|           color: $blue; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     a { |  | ||||||
|       color: $grey-dark; |  | ||||||
|       padding-left: $size-5; |  | ||||||
|       transition: 250ms border-width; |  | ||||||
|  |  | ||||||
|       &.active { |  | ||||||
|         border-right: 4px solid $blue; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     // TODO will be removed with the navbar work. Reminder to remove the var $fullhd as this is the only use case. |  | ||||||
|     .tag { |  | ||||||
|       @include from($fullhd) { |  | ||||||
|         float: right; |  | ||||||
|     } |     } | ||||||
|  |     .token-alert { | ||||||
|  |       padding: $spacing-xs; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .link-status { | ||||||
|  |   height: 40px; | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  |   align-items: center; | ||||||
|  |   font-size: $size-7; | ||||||
|  |   font-weight: $font-weight-semibold; | ||||||
|  |  | ||||||
|  |   &.connected { | ||||||
|  |     background-color: var(--token-color-surface-action); | ||||||
|  |     color: var(--token-color-foreground-action-active); | ||||||
|  |  | ||||||
|  |     a { | ||||||
|  |       color: var(--token-color-foreground-action-active); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   &.warning { | ||||||
|  |     background-color: var(--token-color-surface-warning); | ||||||
|  |     color: var(--token-color-palette-amber-300); | ||||||
|  |  | ||||||
|  |     a { | ||||||
|  |       color: var(--token-color-palette-amber-300); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|  * SPDX-License-Identifier: MPL-2.0 |  * SPDX-License-Identifier: MPL-2.0 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| .navbar-brand .splash-page-logo { | .splash-page-logo { | ||||||
|   padding: $spacing-xs $spacing-s $spacing-xs $spacing-l; |   padding: $spacing-xs $spacing-s $spacing-xs $spacing-l; | ||||||
|  |  | ||||||
|   @include from($mobile) { |   @include from($mobile) { | ||||||
|   | |||||||
| @@ -1,24 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| .status-indicator-button { |  | ||||||
|   &[data-status='good'] { |  | ||||||
|     .status-indicator-color { |  | ||||||
|       color: $green-light; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   &[data-status='mixed'] { |  | ||||||
|     .status-indicator-color { |  | ||||||
|       color: $yellow; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   &[data-status='bad'] { |  | ||||||
|     .status-indicator-color { |  | ||||||
|       color: $red; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -40,13 +40,12 @@ | |||||||
|       &.is-active { |       &.is-active { | ||||||
|         border-bottom: 2px solid $blue; |         border-bottom: 2px solid $blue; | ||||||
|         color: $blue; |         color: $blue; | ||||||
|       } | 
 | ||||||
|       // solves for tabs on secret engines |         > button { | ||||||
|       > a &.active { |  | ||||||
|         border-bottom: 2px solid $blue; |  | ||||||
|           color: $blue; |           color: $blue; | ||||||
|         } |         } | ||||||
|       // solves for tabs on auth mounts |       } | ||||||
|  |       // solves for tabs on auth mounts & secrets engines | ||||||
|       > a { |       > a { | ||||||
|         &.active { |         &.active { | ||||||
|           color: $blue; |           color: $blue; | ||||||
| @@ -36,7 +36,6 @@ | |||||||
| @import './core/lists'; | @import './core/lists'; | ||||||
| @import './core/menu'; | @import './core/menu'; | ||||||
| @import './core/message'; | @import './core/message'; | ||||||
| @import './core/navbar'; |  | ||||||
| @import './core/progress'; | @import './core/progress'; | ||||||
| @import './core/select'; | @import './core/select'; | ||||||
| @import './core/switch'; | @import './core/switch'; | ||||||
| @@ -66,7 +65,7 @@ | |||||||
| @import './components/control-group'; | @import './components/control-group'; | ||||||
| @import './components/diff-version-selector'; | @import './components/diff-version-selector'; | ||||||
| @import './components/doc-link'; | @import './components/doc-link'; | ||||||
| @import './components/empty-state'; | @import './components/empty-state-component'; | ||||||
| @import './components/env-banner'; | @import './components/env-banner'; | ||||||
| @import './components/features-selection'; | @import './components/features-selection'; | ||||||
| @import './components/form-section'; | @import './components/form-section'; | ||||||
| @@ -85,7 +84,7 @@ | |||||||
| @import './components/loader'; | @import './components/loader'; | ||||||
| @import './components/login-form'; | @import './components/login-form'; | ||||||
| @import './components/masked-input'; | @import './components/masked-input'; | ||||||
| @import './components/modal'; | @import './components/modal-component.scss'; | ||||||
| @import './components/namespace-picker'; | @import './components/namespace-picker'; | ||||||
| @import './components/namespace-reminder'; | @import './components/namespace-reminder'; | ||||||
| @import './components/navigate-input'; | @import './components/navigate-input'; | ||||||
| @@ -112,8 +111,7 @@ | |||||||
| @import './components/sidebar'; | @import './components/sidebar'; | ||||||
| @import './components/splash-page'; | @import './components/splash-page'; | ||||||
| @import './components/stat-text'; | @import './components/stat-text'; | ||||||
| @import './components/status-menu'; | @import './components/tabs-component'; | ||||||
| @import './components/tabs'; |  | ||||||
| @import './components/text-file'; | @import './components/text-file'; | ||||||
| @import './components/token-expire-warning'; | @import './components/token-expire-warning'; | ||||||
| @import './components/toolbar'; | @import './components/toolbar'; | ||||||
|   | |||||||
| @@ -15,12 +15,6 @@ | |||||||
|     margin: 0; |     margin: 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   .is-sidebar + .column & { |  | ||||||
|     @include until($mobile) { |  | ||||||
|       margin-left: $size-2; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ul, |   ul, | ||||||
|   ol { |   ol { | ||||||
|     align-items: center; |     align-items: center; | ||||||
|   | |||||||
| @@ -11,10 +11,9 @@ | |||||||
| } | } | ||||||
|  |  | ||||||
| .page-container { | .page-container { | ||||||
|   min-height: calc(100vh - 4rem); |   min-height: 100vh; | ||||||
|   display: flex; |   display: flex; | ||||||
|   flex-direction: column; |   flex-direction: column; | ||||||
|   margin-top: 4rem; |  | ||||||
|   justify-content: flex-end; |   justify-content: flex-end; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -39,30 +38,7 @@ | |||||||
| .container { | .container { | ||||||
|   flex-grow: 1; |   flex-grow: 1; | ||||||
|   margin: 0 auto; |   margin: 0 auto; | ||||||
|  |   max-width: 1024px; | ||||||
|   position: relative; |   position: relative; | ||||||
|   width: auto; |   width: auto; | ||||||
| } | } | ||||||
|  |  | ||||||
| @media screen and (min-width: 1024px) { |  | ||||||
|   .container { |  | ||||||
|     max-width: 960px; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @media screen and (max-width: 1215px) { |  | ||||||
|   .container.is-widescreen:not(.is-max-desktop) { |  | ||||||
|     max-width: 1152px; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @media screen and (min-width: 1216px) { |  | ||||||
|   .container:not(.is-max-desktop) { |  | ||||||
|     max-width: 1152px; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @media screen and (min-width: 1408px) { |  | ||||||
|   .container:not(.is-max-desktop):not(.is-max-widescreen) { |  | ||||||
|     max-width: 1344px; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -17,10 +17,10 @@ | |||||||
|   height: 2.25em; |   height: 2.25em; | ||||||
|   justify-content: flex-start; |   justify-content: flex-start; | ||||||
|   min-width: auto; |   min-width: auto; | ||||||
|   padding-bottom: calc(0.375em -1px); |   padding-bottom: calc(0.375em - 1px); | ||||||
|   padding-left: 1em; |   padding-left: 1em; | ||||||
|   padding-right: 1em; |   padding-right: 1em; | ||||||
|   padding-top: calc(0.375em -1px); |   padding-top: calc(0.375em - 1px); | ||||||
|   position: relative; |   position: relative; | ||||||
|   vertical-align: top; |   vertical-align: top; | ||||||
|   white-space: nowrap; |   white-space: nowrap; | ||||||
|   | |||||||
| @@ -1,309 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: MPL-2.0 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| .navbar { |  | ||||||
|   left: 0; |  | ||||||
|   position: fixed; |  | ||||||
|   right: 0; |  | ||||||
|   top: 0; |  | ||||||
|   @include from($mobile) { |  | ||||||
|     display: block; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-status { |  | ||||||
|   height: 40px; |  | ||||||
|   display: flex; |  | ||||||
|   justify-content: center; |  | ||||||
|   align-items: center; |  | ||||||
|   font-size: $size-7; |  | ||||||
|   font-weight: $font-weight-semibold; |  | ||||||
|  |  | ||||||
|   &.connected { |  | ||||||
|     background-color: $ui-gray-800; |  | ||||||
|     color: $ui-gray-300; |  | ||||||
|  |  | ||||||
|     a { |  | ||||||
|       color: #c2c5cb; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   &.warning { |  | ||||||
|     background-color: #fcf6ea; |  | ||||||
|     color: #975b06; |  | ||||||
|  |  | ||||||
|     a { |  | ||||||
|       color: #975b06; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-actions { |  | ||||||
|   background-color: $black; |  | ||||||
|   display: flex; |  | ||||||
|   height: 4rem; |  | ||||||
|   justify-content: flex-start; |  | ||||||
|   padding: $spacing-xs $spacing-s $spacing-xs 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-brand { |  | ||||||
|   align-items: stretch; |  | ||||||
|   background: $grey; |  | ||||||
|   border-radius: 0 $radius-large $radius-large 0; |  | ||||||
|   display: flex; |  | ||||||
|   margin-right: $spacing-s; |  | ||||||
|   min-height: auto; |  | ||||||
|   position: relative; |  | ||||||
|   z-index: 203; |  | ||||||
|  |  | ||||||
|   .navbar-item { |  | ||||||
|     align-items: center; |  | ||||||
|     background-color: transparent; |  | ||||||
|     display: flex; |  | ||||||
|     padding: $spacing-xs $spacing-l; |  | ||||||
|  |  | ||||||
|     &:hover, |  | ||||||
|     &.is-active { |  | ||||||
|       background-color: transparent; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-drawer-toggle { |  | ||||||
|   font-size: $size-6; |  | ||||||
|   color: $grey; |  | ||||||
|   cursor: pointer; |  | ||||||
|   font-weight: $font-weight-semibold; |  | ||||||
|   margin-left: -$spacing-s; |  | ||||||
|   padding: $spacing-xs $spacing-xxs; |  | ||||||
|   background: none; |  | ||||||
|   border: none; |  | ||||||
|  |  | ||||||
|   .navbar-drawer & { |  | ||||||
|     position: absolute; |  | ||||||
|     top: $spacing-xs; |  | ||||||
|     right: $spacing-xxs; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-drawer-overlay { |  | ||||||
|   height: 100vh; |  | ||||||
|   left: 0; |  | ||||||
|   pointer-events: none; |  | ||||||
|   position: fixed; |  | ||||||
|   right: 0; |  | ||||||
|   top: 0; |  | ||||||
|   transition: background-color $speed, opacity $speed; |  | ||||||
|   will-change: background-color, opacity; |  | ||||||
|   z-index: -1; |  | ||||||
|  |  | ||||||
|   &.is-active { |  | ||||||
|     background-color: rgba($black, 0.25); |  | ||||||
|     pointer-events: all; |  | ||||||
|  |  | ||||||
|     @include from($mobile) { |  | ||||||
|       background-color: transparent; |  | ||||||
|       pointer-events: none; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-sections, |  | ||||||
| .navbar-sections li, |  | ||||||
| .navbar-drawer-scroll, |  | ||||||
| .navbar-drawer-scroll > * { |  | ||||||
|   @include from($mobile) { |  | ||||||
|     align-items: center; |  | ||||||
|     display: flex; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-sections { |  | ||||||
|   a { |  | ||||||
|     color: $grey-light; |  | ||||||
|     display: block; |  | ||||||
|     font-weight: $font-weight-semibold; |  | ||||||
|     line-height: 1.3; |  | ||||||
|     padding: $spacing-xs $spacing-m; |  | ||||||
|     text-decoration: none; |  | ||||||
|     transition: background-color $speed, color $speed; |  | ||||||
|     will-change: background-color, color; |  | ||||||
|  |  | ||||||
|     @include from($mobile) { |  | ||||||
|       border-radius: $radius; |  | ||||||
|       display: inline-block; |  | ||||||
|       padding: $spacing-xxs $spacing-s; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &.is-active { |  | ||||||
|       background-color: $ui-gray-800; |  | ||||||
|       color: $white; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &:hover { |  | ||||||
|       color: $white; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-end { |  | ||||||
|   margin-left: auto; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-item { |  | ||||||
|   padding: $spacing-xs; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-separator { |  | ||||||
|   background-color: $ui-gray-700; |  | ||||||
|   height: 1px; |  | ||||||
|   margin: $spacing-xs 0; |  | ||||||
|   width: 100%; |  | ||||||
|  |  | ||||||
|   @include from($mobile) { |  | ||||||
|     height: $spacing-l; |  | ||||||
|     margin: 0 $spacing-s; |  | ||||||
|     width: 1px; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-drawer { |  | ||||||
|   flex: 1 1 auto; |  | ||||||
|  |  | ||||||
|   @include until($mobile) { |  | ||||||
|     background-color: $ui-gray-900; |  | ||||||
|     display: flex; |  | ||||||
|     flex-direction: column; |  | ||||||
|     height: 100vh; |  | ||||||
|     left: 0; |  | ||||||
|     padding: 4rem 0 $spacing-m; |  | ||||||
|     position: fixed; |  | ||||||
|     top: 0; |  | ||||||
|     transform: translateX(-100%); |  | ||||||
|     transition: box-shadow $speed, transform $speed-slow; |  | ||||||
|     width: $drawer-width; |  | ||||||
|     will-change: transform, box-shadow; |  | ||||||
|     z-index: 201; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   &.is-active { |  | ||||||
|     @include until($mobile) { |  | ||||||
|       box-shadow: 5px 0 10px rgba($black, 0.36); |  | ||||||
|       transform: translateX(0); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .navbar-item .button { |  | ||||||
|     color: $grey-light; |  | ||||||
|     display: flex; |  | ||||||
|     font-size: 1rem; |  | ||||||
|     height: auto; |  | ||||||
|     justify-content: flex-start; |  | ||||||
|     text-align: left; |  | ||||||
|     width: 100%; |  | ||||||
|  |  | ||||||
|     @include from($mobile) { |  | ||||||
|       display: inline-flex; |  | ||||||
|       height: $spacing-l; |  | ||||||
|       width: auto; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     &.popup-open, |  | ||||||
|     &.ember-basic-dropdown-trigger--below { |  | ||||||
|       color: $white; |  | ||||||
|  |  | ||||||
|       .is-status-chevron { |  | ||||||
|         transform: rotate(0deg); |  | ||||||
|  |  | ||||||
|         @include from($mobile) { |  | ||||||
|           transform: rotate(180deg); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     .is-status-chevron { |  | ||||||
|       transform: rotate(270deg); |  | ||||||
|  |  | ||||||
|       @include from($mobile) { |  | ||||||
|         transform: rotate(0deg); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .button .icon, |  | ||||||
|   .button .icon:first-child:not(:last-child) { |  | ||||||
|     flex: 0; |  | ||||||
|     margin: 0 $spacing-xs 0 0; |  | ||||||
|  |  | ||||||
|     @include from($mobile) { |  | ||||||
|       margin: -$spacing-xxs; |  | ||||||
|       margin-right: 0; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .status-menu-label { |  | ||||||
|     flex: 1 1 auto; |  | ||||||
|     line-height: 1; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   .nav-console-button .status-menu-label, |  | ||||||
|   .nav-user-button .status-menu-label { |  | ||||||
|     flex: 1 1 auto; |  | ||||||
|  |  | ||||||
|     @include from($mobile) { |  | ||||||
|       display: none; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .nav-user-button .icon { |  | ||||||
|   position: relative; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .nav-user-button.may-expire .icon:first-of-type::after { |  | ||||||
|   content: ''; |  | ||||||
|   position: absolute; |  | ||||||
|   top: 0; |  | ||||||
|   right: 0; |  | ||||||
|   height: 6px; |  | ||||||
|   width: 6px; |  | ||||||
|   border-radius: 50%; |  | ||||||
|   background: $yellow; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-drawer-scroll { |  | ||||||
|   overflow: auto; |  | ||||||
|   height: 100%; |  | ||||||
|   -webkit-overflow-scrolling: touch; |  | ||||||
|  |  | ||||||
|   &::before { |  | ||||||
|     background-image: linear-gradient(to bottom, $ui-gray-900, rgba($ui-gray-900, 0)); |  | ||||||
|     content: ''; |  | ||||||
|     height: $spacing-xs; |  | ||||||
|     left: 0; |  | ||||||
|     position: absolute; |  | ||||||
|     right: 0; |  | ||||||
|     top: 4rem; |  | ||||||
|     z-index: 1; |  | ||||||
|  |  | ||||||
|     @include from($mobile) { |  | ||||||
|       display: none; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .navbar-drawer .ember-basic-dropdown-content { |  | ||||||
|   @include until($mobile) { |  | ||||||
|     position: relative; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // responsive css |  | ||||||
| @media screen and (min-width: 1024px) { |  | ||||||
|   .navbar-item, |  | ||||||
|   .navbar-link { |  | ||||||
|     align-items: center; |  | ||||||
|     display: flex; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -95,7 +95,7 @@ | |||||||
|       content: ''; |       content: ''; | ||||||
|       height: $size-8; |       height: $size-8; | ||||||
|       position: absolute; |       position: absolute; | ||||||
|       top: $size-8/ 5; |       top: calc($size-8 / 5); | ||||||
|       width: $size-8 * 2; |       width: $size-8 * 2; | ||||||
|     } |     } | ||||||
|     &::after { |     &::after { | ||||||
| @@ -106,7 +106,7 @@ | |||||||
|       height: $size-8 * 0.8; |       height: $size-8 * 0.8; | ||||||
|       left: 0; |       left: 0; | ||||||
|       position: absolute; |       position: absolute; | ||||||
|       top: $size-8/ 4; |       top: calc($size-8 / 4); | ||||||
|       transform: translateX(0.15rem); |       transform: translateX(0.15rem); | ||||||
|       transition: all 0.25s ease-out; |       transition: all 0.25s ease-out; | ||||||
|       width: $size-8 * 0.8; |       width: $size-8 * 0.8; | ||||||
|   | |||||||
| @@ -74,7 +74,7 @@ | |||||||
|       padding-left: $size-8 * 2.5; |       padding-left: $size-8 * 2.5; | ||||||
|       margin: 0 0.25rem; |       margin: 0 0.25rem; | ||||||
|       &::before { |       &::before { | ||||||
|         top: $size-8 / 5; |         top: calc($size-8 / 5); | ||||||
|         height: $size-8; |         height: $size-8; | ||||||
|         width: $size-8 * 2; |         width: $size-8 * 2; | ||||||
|       } |       } | ||||||
| @@ -83,7 +83,7 @@ | |||||||
|         height: $size-8 * 0.8; |         height: $size-8 * 0.8; | ||||||
|         transform: translateX(0.15rem); |         transform: translateX(0.15rem); | ||||||
|         left: 0; |         left: 0; | ||||||
|         top: $size-8/ 4; |         top: calc($size-8 / 4); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     &:checked + label::after { |     &:checked + label::after { | ||||||
|   | |||||||
| @@ -34,6 +34,10 @@ | |||||||
|   padding-bottom: $spacing-s; |   padding-bottom: $spacing-s; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .has-bottom-padding-l { | ||||||
|  |   padding-bottom: $spacing-l; | ||||||
|  | } | ||||||
|  |  | ||||||
| .has-top-padding-s { | .has-top-padding-s { | ||||||
|   padding-top: $spacing-s; |   padding-top: $spacing-s; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -79,6 +79,10 @@ | |||||||
|   text-align: center !important; |   text-align: center !important; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .has-line-height-1 { | ||||||
|  |   line-height: 1; | ||||||
|  | } | ||||||
|  |  | ||||||
| // Text color or styling | // Text color or styling | ||||||
| .is-help { | .is-help { | ||||||
|   font-size: $size-8; |   font-size: $size-8; | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								ui/app/styles/reset.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ui/app/styles/reset.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | // reset for HDS | ||||||
|  | *, | ||||||
|  | *::before, | ||||||
|  | *::after { | ||||||
|  |   box-sizing: border-box; | ||||||
|  | } | ||||||
| @@ -6,12 +6,12 @@ | |||||||
| /* General sizing in rem values used largely for text sizing.*/ | /* General sizing in rem values used largely for text sizing.*/ | ||||||
| $size-1: 3rem; // 48px, same as $spacing-xxl | $size-1: 3rem; // 48px, same as $spacing-xxl | ||||||
| $size-2: 2.5rem; // 40px | $size-2: 2.5rem; // 40px | ||||||
| $size-3: (24/14) + 0rem; // ~1.714rem ~27px | $size-3: calc(24 / 14) + 0rem; // ~1.714rem ~27px | ||||||
| $size-4: 1.5rem; // 24px same as $spacing-l | $size-4: 1.5rem; // 24px same as $spacing-l | ||||||
| $size-5: 1.25rem; // 20px | $size-5: 1.25rem; // 20px | ||||||
| $size-6: 1rem; // 16px same as $spacing-m | $size-6: 1rem; // 16px same as $spacing-m | ||||||
| $size-7: (13/14) + 0rem; // ~.929rem ~15px | $size-7: calc(13 / 14) + 0rem; // ~.929rem ~15px | ||||||
| $size-8: (12/14) + 0rem; // ~.857rem  ~13.7px | $size-8: calc(12 / 14) + 0rem; // ~.857rem  ~13.7px | ||||||
| $size-9: 0.75rem; // 12px same as $spacing-s | $size-9: 0.75rem; // 12px same as $spacing-s | ||||||
| $size-10: 0.5rem; // 8px same as $spacing-xs | $size-10: 0.5rem; // 8px same as $spacing-xs | ||||||
| $size-11: 0.25rem; // 4px same as spacing-xxs | $size-11: 0.25rem; // 4px same as spacing-xxs | ||||||
|   | |||||||
| @@ -84,6 +84,6 @@ | |||||||
|  |  | ||||||
| @mixin vault-block { | @mixin vault-block { | ||||||
|   &:not(:last-child) { |   &:not(:last-child) { | ||||||
|     margin-bottom: (5/14) + 0rem; |     margin-bottom: calc(5 / 14) + 0rem; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
| <div class="page-container"> | <Sidebar::Frame @showSidebar={{this.auth.isActiveSession}}> | ||||||
|  |   <div class="page-container"> | ||||||
|     {{outlet}} |     {{outlet}} | ||||||
| </div> |   </div> | ||||||
|  | </Sidebar::Frame> | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| {{#unless this.selectedAuthIsPath}} | {{#unless this.selectedAuthIsPath}} | ||||||
|   <div class="box has-slim-padding is-shadowless"> |   <div class="box has-slim-padding is-shadowless"> | ||||||
|     <ToggleButton @isOpen={{this.isOpen}} @onClick={{fn (mut this.isOpen)}} /> |     <ToggleButton @isOpen={{this.isOpen}} @onClick={{fn (mut this.isOpen)}} data-test-auth-form-options-toggle /> | ||||||
|     {{#if this.isOpen}} |     {{#if this.isOpen}} | ||||||
|       <div class="field"> |       <div class="field"> | ||||||
|         <label for="custom-path" class="is-label"> |         <label for="custom-path" class="is-label"> | ||||||
| @@ -14,6 +14,7 @@ | |||||||
|             class="input" |             class="input" | ||||||
|             value={{@customPath}} |             value={{@customPath}} | ||||||
|             oninput={{action @onPathChange value="target.value"}} |             oninput={{action @onPathChange value="target.value"}} | ||||||
|  |             data-test-auth-form-mount-path | ||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|         <AlertInline |         <AlertInline | ||||||
|   | |||||||
| @@ -1,81 +0,0 @@ | |||||||
| <Confirm as |c|> |  | ||||||
|   <div class="popup-menu-content"> |  | ||||||
|     <div class="box"> |  | ||||||
|       <div class="menu-label"> |  | ||||||
|         {{this.auth.authData.displayName}} |  | ||||||
|       </div> |  | ||||||
|       <nav class="menu"> |  | ||||||
|         <ul class="menu-list"> |  | ||||||
|           {{#if this.auth.allowExpiration}} |  | ||||||
|             <li class="action"> |  | ||||||
|               <AlertBanner |  | ||||||
|                 @type="warning" |  | ||||||
|                 @message="We've stopped auto-renewing your token due to inactivity. |  | ||||||
|                 It will expire in {{date-from-now this.auth.tokenExpirationDate interval=1000 hideSuffix=true}}. |  | ||||||
|                 On {{date-format this.auth.tokenExpirationDate 'MMMM do yyyy, h:mm:ss a'}}" |  | ||||||
|               /> |  | ||||||
|             </li> |  | ||||||
|           {{/if}} |  | ||||||
|           {{#if this.hasEntityId}} |  | ||||||
|             <li class="action"> |  | ||||||
|               <LinkTo @route="vault.cluster.mfa-setup" data-test-status-link="mfa"> |  | ||||||
|                 Multi-factor authentication |  | ||||||
|               </LinkTo> |  | ||||||
|             </li> |  | ||||||
|           {{/if}} |  | ||||||
|           <li class="action"> |  | ||||||
|             <CopyButton |  | ||||||
|               @clipboardText={{this.auth.currentToken}} |  | ||||||
|               class="link" |  | ||||||
|               @buttonType="button" |  | ||||||
|               @success={{action (set-flash-message "Token copied!")}} |  | ||||||
|             > |  | ||||||
|               Copy token |  | ||||||
|             </CopyButton> |  | ||||||
|           </li> |  | ||||||
|           {{#if (is-before (now interval=1000) this.auth.tokenExpirationDate)}} |  | ||||||
|             {{#if this.auth.authData.renewable}} |  | ||||||
|               <li class="action"> |  | ||||||
|                 <button type="button" {{on "click" this.renewToken}} class="link button {{if this.isRenewing 'is-loading'}}"> |  | ||||||
|                   Renew token |  | ||||||
|                 </button> |  | ||||||
|               </li> |  | ||||||
|               <li class="action"> |  | ||||||
|                 <c.Message |  | ||||||
|                   @id={{get this.auth "authData.displayName"}} |  | ||||||
|                   @title={{concat "Revoke " (get this.auth "authData.displayName") "?"}} |  | ||||||
|                   @onConfirm={{action "revokeToken"}} |  | ||||||
|                   @message="You will not be able to log in again with this token." |  | ||||||
|                   @triggerText="Revoke token" |  | ||||||
|                   @confirmButtonText="Revoke" |  | ||||||
|                 /> |  | ||||||
|               </li> |  | ||||||
|             {{else}} |  | ||||||
|               <li class="action text-right"> |  | ||||||
|                 <c.Message |  | ||||||
|                   @id={{get this.auth "authData.displayName"}} |  | ||||||
|                   @title={{concat "Revoke " (get this.auth "authData.displayName") "?"}} |  | ||||||
|                   @onConfirm={{action "revokeToken"}} |  | ||||||
|                   @message="You will not be able to log in again with this token." |  | ||||||
|                   @triggerText="Revoke token" |  | ||||||
|                   @confirmButtonText="Revoke" |  | ||||||
|                 /> |  | ||||||
|               </li> |  | ||||||
|             {{/if}} |  | ||||||
|           {{/if}} |  | ||||||
|           <li class="action"> |  | ||||||
|             <LinkTo |  | ||||||
|               @route="vault.cluster.logout" |  | ||||||
|               @model={{@activeClusterName}} |  | ||||||
|               id="logout" |  | ||||||
|               class="is-destroy" |  | ||||||
|               {{on "click" @onLinkClick}} |  | ||||||
|             > |  | ||||||
|               Sign out |  | ||||||
|             </LinkTo> |  | ||||||
|           </li> |  | ||||||
|         </ul> |  | ||||||
|       </nav> |  | ||||||
|     </div> |  | ||||||
|   </div> |  | ||||||
| </Confirm> |  | ||||||
| @@ -1,9 +1,5 @@ | |||||||
| <BasicDropdown @class="popup-menu" @horizontalPosition="auto-right" @verticalPosition="below" as |D|> | <BasicDropdown @class="popup-menu" @horizontalPosition="auto-right" @verticalPosition="below" as |D|> | ||||||
|   <D.Trigger |   <D.Trigger data-test-calendar-widget-trigger class={{concat "toolbar-link" (if D.isOpen " is-active")}} @htmlTag="button"> | ||||||
|     data-test-popup-menu-trigger="true" |  | ||||||
|     class={{concat "toolbar-link" (if D.isOpen " is-active")}} |  | ||||||
|     @htmlTag="button" |  | ||||||
|   > |  | ||||||
|     {{date-format this.startDate "MMM yyyy"}} |     {{date-format this.startDate "MMM yyyy"}} | ||||||
|     - |     - | ||||||
|     {{date-format this.endDate "MMM yyyy"}} |     {{date-format this.endDate "MMM yyyy"}} | ||||||
|   | |||||||
| @@ -1,150 +0,0 @@ | |||||||
| <div class="popup-menu-content"> |  | ||||||
|   <div class="box"> |  | ||||||
|     {{#unless this.version.isOSS}} |  | ||||||
|       {{#if (and this.activeCluster.unsealed this.auth.currentToken)}} |  | ||||||
|         {{#if @cluster.dr.isSecondary}} |  | ||||||
|           {{#if (has-permission "status" routeParams="replication")}} |  | ||||||
|             <nav class="menu" aria-label="replication secondary menu"> |  | ||||||
|               <p class="menu-label">Replication</p> |  | ||||||
|               <ul> |  | ||||||
|                 {{#if @cluster.anyReplicationEnabled}} |  | ||||||
|                   <li> |  | ||||||
|                     <LinkTo |  | ||||||
|                       @route="vault.cluster.replication-dr-promote.details" |  | ||||||
|                       @disabled={{not this.auth.currentToken}} |  | ||||||
|                       {{on "click" @onLinkClick}} |  | ||||||
|                     > |  | ||||||
|                       <ReplicationModeSummary @mode="dr" @display="menu" @cluster={{@cluster}} /> |  | ||||||
|                     </LinkTo> |  | ||||||
|                   </li> |  | ||||||
|                 {{/if}} |  | ||||||
|               </ul> |  | ||||||
|             </nav> |  | ||||||
|             <hr /> |  | ||||||
|           {{/if}} |  | ||||||
|         {{else}} |  | ||||||
|           {{#if (has-permission "status" routeParams="replication")}} |  | ||||||
|             <nav class="menu" aria-label="replication menu"> |  | ||||||
|               <p class="menu-label">Replication</p> |  | ||||||
|               <ul> |  | ||||||
|                 {{#if @cluster.anyReplicationEnabled}} |  | ||||||
|                   <li> |  | ||||||
|                     <LinkTo |  | ||||||
|                       @route="vault.cluster.replication.mode.index" |  | ||||||
|                       @model="dr" |  | ||||||
|                       @disabled={{not this.auth.currentToken}} |  | ||||||
|                       {{on "click" @onLinkClick}} |  | ||||||
|                     > |  | ||||||
|                       <ReplicationModeSummary @mode="dr" @display="menu" @cluster={{@cluster}} /> |  | ||||||
|                     </LinkTo> |  | ||||||
|                   </li> |  | ||||||
|                   <li> |  | ||||||
|                     {{#if (has-feature "Performance Replication")}} |  | ||||||
|                       <LinkTo |  | ||||||
|                         @route="vault.cluster.replication.mode.index" |  | ||||||
|                         @model="performance" |  | ||||||
|                         @disabled={{not this.auth.currentToken}} |  | ||||||
|                         {{on "click" @onLinkClick}} |  | ||||||
|                       > |  | ||||||
|                         <ReplicationModeSummary @mode="performance" @display="menu" @cluster={{@cluster}} @tagName="span" /> |  | ||||||
|                       </LinkTo> |  | ||||||
|                     {{else}} |  | ||||||
|                       <ReplicationModeSummary @mode="performance" @display="menu" @cluster={{@cluster}} @class="menu-item" /> |  | ||||||
|                     {{/if}} |  | ||||||
|                   </li> |  | ||||||
|                 {{else}} |  | ||||||
|                   <li> |  | ||||||
|                     <LinkTo @route="vault.cluster.replication" {{on "click" @onLinkClick}}> |  | ||||||
|                       <div class="level is-mobile"> |  | ||||||
|                         <span class="level-left">Enable</span> |  | ||||||
|                         <Icon @name="plus-circle" class="has-text-grey-light level-right" /> |  | ||||||
|                       </div> |  | ||||||
|                     </LinkTo> |  | ||||||
|                   </li> |  | ||||||
|                 {{/if}} |  | ||||||
|               </ul> |  | ||||||
|             </nav> |  | ||||||
|             <hr /> |  | ||||||
|           {{/if}} |  | ||||||
|         {{/if}} |  | ||||||
|       {{/if}} |  | ||||||
|     {{/unless}} |  | ||||||
|     <nav class="menu" aria-label="server menu"> |  | ||||||
|       <div class="menu-label"> |  | ||||||
|         Server |  | ||||||
|       </div> |  | ||||||
|       <ul class="menu-list"> |  | ||||||
|         <li class="action"> |  | ||||||
|           {{#if this.activeCluster.unsealed}} |  | ||||||
|             {{#if (and (has-permission "status" routeParams="seal") (not @cluster.dr.isSecondary))}} |  | ||||||
|               <LinkTo @route="vault.cluster.settings.seal" @model={{@cluster.name}} {{on "click" @onLinkClick}}> |  | ||||||
|                 <div class="level is-mobile"> |  | ||||||
|                   <span class="level-left">Unsealed</span> |  | ||||||
|                   <Icon @name="check-circle" class="has-text-success level-right" /> |  | ||||||
|                 </div> |  | ||||||
|               </LinkTo> |  | ||||||
|             {{else}} |  | ||||||
|               <span class="menu-item"> |  | ||||||
|                 <div class="level is-mobile"> |  | ||||||
|                   <span class="level-left">Unsealed</span> |  | ||||||
|                   <Icon @name="check-circle" class="has-text-success level-right" /> |  | ||||||
|                 </div> |  | ||||||
|               </span> |  | ||||||
|             {{/if}} |  | ||||||
|           {{else}} |  | ||||||
|             <span class="menu-item"> |  | ||||||
|               <div class="level is-mobile"> |  | ||||||
|                 <span class="level-left has-text-danger">Sealed</span> |  | ||||||
|                 <Icon @name="x-circle" class="has-text-danger level-right" /> |  | ||||||
|               </div> |  | ||||||
|             </span> |  | ||||||
|           {{/if}} |  | ||||||
|         </li> |  | ||||||
|       </ul> |  | ||||||
|       {{#if |  | ||||||
|         (and |  | ||||||
|           (or |  | ||||||
|             (and this.version.features (has-permission "status" routeParams="license")) |  | ||||||
|             (and @cluster.usingRaft (has-permission "status" routeParams="raft")) |  | ||||||
|           ) |  | ||||||
|           (not @cluster.dr.isSecondary) |  | ||||||
|         ) |  | ||||||
|       }} |  | ||||||
|         <ul class="menu-list"> |  | ||||||
|           {{#if (and this.version.features (has-permission "status" routeParams="license") (not @cluster.dr.isSecondary))}} |  | ||||||
|             <li class="action"> |  | ||||||
|               <LinkTo @route="vault.cluster.license" @model={{this.activeCluster.name}} {{on "click" @onLinkClick}}> |  | ||||||
|                 <div class="level is-mobile"> |  | ||||||
|                   <span class="level-left">License</span> |  | ||||||
|                   <Chevron class="has-text-grey-light level-right" /> |  | ||||||
|                 </div> |  | ||||||
|               </LinkTo> |  | ||||||
|             </li> |  | ||||||
|           {{/if}} |  | ||||||
|           {{#if (and @cluster.usingRaft (has-permission "status" routeParams="raft"))}} |  | ||||||
|             <li class="action"> |  | ||||||
|               <LinkTo @route="vault.cluster.storage" @model={{this.activeCluster.name}} {{on "click" @onLinkClick}}> |  | ||||||
|                 <div class="level is-mobile"> |  | ||||||
|                   <span class="level-left">Raft Storage</span> |  | ||||||
|                   <Chevron class="has-text-grey-light level-right" /> |  | ||||||
|                 </div> |  | ||||||
|               </LinkTo> |  | ||||||
|             </li> |  | ||||||
|           {{/if}} |  | ||||||
|         </ul> |  | ||||||
|       {{/if}} |  | ||||||
|       {{#if (and (has-permission "clients" routeParams="activity") (not @cluster.dr.isSecondary) this.auth.currentToken)}} |  | ||||||
|         <ul class="menu-list"> |  | ||||||
|           <li class="action"> |  | ||||||
|             <LinkTo @route="vault.cluster.clients.dashboard" {{on "click" @onLinkClick}}> |  | ||||||
|               <div class="level is-mobile"> |  | ||||||
|                 <span class="level-left">Client count</span> |  | ||||||
|                 <Chevron class="has-text-grey-light level-right" /> |  | ||||||
|               </div> |  | ||||||
|             </LinkTo> |  | ||||||
|           </li> |  | ||||||
|         </ul> |  | ||||||
|       {{/if}} |  | ||||||
|     </nav> |  | ||||||
|   </div> |  | ||||||
| </div> |  | ||||||
| @@ -5,7 +5,7 @@ | |||||||
|     <Chevron /> |     <Chevron /> | ||||||
|   {{/if}} |   {{/if}} | ||||||
|   <input onkeyup={{action "handleKeyUp"}} value={{this.value}} autocomplete="off" spellcheck="false" /> |   <input onkeyup={{action "handleKeyUp"}} value={{this.value}} autocomplete="off" spellcheck="false" /> | ||||||
|   <ToolTip @horizontalPosition="auto-right" @verticalPosition={{if this.isFullscreen "above" "below"}} as |d|> |   <ToolTip @horizontalPosition="auto-right" @verticalPosition="above" as |d|> | ||||||
|     <d.Trigger> |     <d.Trigger> | ||||||
|       <button |       <button | ||||||
|         type="button" |         type="button" | ||||||
|   | |||||||
| @@ -1,2 +1,4 @@ | |||||||
| {{! using Icon here instead of Chevron because two nested tagless components results in a rendered line break between the tags breaking the layout in the <pre> }} | <div class="is-flex-center"> | ||||||
| <p class="console-ui-command is-font-mono"><Icon @name="chevron-right" />{{@content}}</p> |   <Icon @name="chevron-right" /> | ||||||
|  |   <pre class="console-ui-command">{{@content}}</pre> | ||||||
|  | </div> | ||||||
| @@ -1,6 +1,8 @@ | |||||||
| <button type="button" class="button is-ghost console-close-button" {{action "closeConsole"}}> | <div class="console-close-button"> | ||||||
|  |   <button type="button" class="button is-ghost" {{action "closeConsole"}} data-test-console-panel-close> | ||||||
|     <Icon @name="x" aria-label="Close console" /> |     <Icon @name="x" aria-label="Close console" /> | ||||||
| </button> |   </button> | ||||||
|  | </div> | ||||||
| <div class="console-ui-panel-content"> | <div class="console-ui-panel-content"> | ||||||
|   <div class="content has-bottom-margin-l"> |   <div class="content has-bottom-margin-l"> | ||||||
|     <p class="has-text-grey is-font-mono"> |     <p class="has-text-grey is-font-mono"> | ||||||
|   | |||||||
| @@ -1,7 +0,0 @@ | |||||||
| <span class={{@class}}> |  | ||||||
|   {{#if (has-block)}} |  | ||||||
|     {{yield}} |  | ||||||
|   {{else}} |  | ||||||
|     {{@text}} |  | ||||||
|   {{/if}} |  | ||||||
| </span> |  | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| {{#if (and this.state this.version.isEnterprise)}} | {{#if (and this.state this.version.isEnterprise)}} | ||||||
|   <div class="navbar-status {{if (eq this.state 'connected') 'connected' 'warning'}}"> |   <div class="link-status {{if (eq this.state 'connected') 'connected' 'warning'}}"> | ||||||
|     <Icon @name="info" /> |     <Icon @name="info" /> | ||||||
|     <p data-test-link-status> |     <p data-test-link-status> | ||||||
|       {{#if (eq this.state "connected")}} |       {{#if (eq this.state "connected")}} | ||||||
|   | |||||||
| @@ -1,21 +0,0 @@ | |||||||
| <aside class="menu"> |  | ||||||
|   {{#if this.title}} |  | ||||||
|     <p class="menu-label"> |  | ||||||
|       {{this.title}} |  | ||||||
|     </p> |  | ||||||
|   {{/if}} |  | ||||||
|   <ul class="menu-list"> |  | ||||||
|     {{yield}} |  | ||||||
|   </ul> |  | ||||||
|   <div class="menu-toggle"> |  | ||||||
|     {{#if this.isActive}} |  | ||||||
|       <button type="button" class="button is-ghost" {{action "closeMenu"}}> |  | ||||||
|         <Icon @name="x" aria-label="Close menu" /> |  | ||||||
|       </button> |  | ||||||
|     {{else}} |  | ||||||
|       <button type="button" class="button is-ghost has-text-grey-light" {{action "openMenu"}}> |  | ||||||
|         <Icon @name="more-vertical" aria-label="Open menu" /> |  | ||||||
|       </button> |  | ||||||
|     {{/if}} |  | ||||||
|   </div> |  | ||||||
| </aside> |  | ||||||
| @@ -1,37 +1,18 @@ | |||||||
| {{#if (and (not this.accessibleNamespaces.length) this.inRootNamespace)}} | <div class="namespace-picker" ...attributes> | ||||||
|   <div class="namespace-picker no-namespaces"> |   <BasicDropdown @horizontalPosition="left" @verticalPosition="above" as |D|> | ||||||
|     {{! Just yield the logo if they're in the root namespace and only have access to it }} |  | ||||||
|     {{yield}} |  | ||||||
|   </div> |  | ||||||
| {{else}} |  | ||||||
|   <div class="namespace-picker"> |  | ||||||
|     <BasicDropdown @horizontalPosition="auto-left" @verticalPosition="below" as |D|> |  | ||||||
|     <D.Trigger |     <D.Trigger | ||||||
|       @htmlTag="button" |       @htmlTag="button" | ||||||
|       class="button is-transparent namespace-picker-trigger has-current-color" |       class="button is-transparent namespace-picker-trigger has-current-color" | ||||||
|       data-test-namespace-toggle |       data-test-namespace-toggle | ||||||
|     > |     > | ||||||
|         {{yield}} |       <div class="is-flex-center"> | ||||||
|         {{#if this.namespaceDisplay}} |         <Icon @name="org" /> | ||||||
|         <span class="namespace-name">{{this.namespaceDisplay}}</span> |         <span class="namespace-name">{{this.namespaceDisplay}}</span> | ||||||
|         {{else}} |       </div> | ||||||
|           <span class="namespace-name is-hidden-tablet">/ (Root)</span> |       <Icon @name="caret" /> | ||||||
|         {{/if}} |  | ||||||
|         <Chevron @direction="down" @class="has-text-white auto-width is-status-chevron" /> |  | ||||||
|     </D.Trigger> |     </D.Trigger> | ||||||
|     <D.Content @defaultClass="namespace-picker-content"> |     <D.Content @defaultClass="namespace-picker-content"> | ||||||
|       <div class="is-mobile level-left"> |       <div class="is-mobile level-left"> | ||||||
|           {{#unless this.isUserRootNamespace}} |  | ||||||
|             <NamespaceLink |  | ||||||
|               @targetNamespace={{or |  | ||||||
|                 (object-at (dec 2 this.menuLeaves.length) this.lastMenuLeaves) |  | ||||||
|                 this.auth.authData.userRootNamespace |  | ||||||
|               }} |  | ||||||
|               @class="namespace-link is-current button is-ghost icon" |  | ||||||
|             > |  | ||||||
|               <Chevron @direction="left" @class="has-text-grey" /> |  | ||||||
|             </NamespaceLink> |  | ||||||
|           {{/unless}} |  | ||||||
|         <h5 class="list-header">Current namespace</h5> |         <h5 class="list-header">Current namespace</h5> | ||||||
|       </div> |       </div> | ||||||
|       <div class="namespace-header-bar level is-mobile"> |       <div class="namespace-header-bar level is-mobile"> | ||||||
| @@ -41,9 +22,6 @@ | |||||||
|               <span class="level-left" data-test-current-namespace> |               <span class="level-left" data-test-current-namespace> | ||||||
|                 {{if this.namespacePath (concat this.namespacePath "/") "root"}} |                 {{if this.namespacePath (concat this.namespacePath "/") "root"}} | ||||||
|               </span> |               </span> | ||||||
|                 <span class="level-right"> |  | ||||||
|                   <Icon @name="check-circle" class="has-text-success" /> |  | ||||||
|                 </span> |  | ||||||
|             </div> |             </div> | ||||||
|           </header> |           </header> | ||||||
|         </div> |         </div> | ||||||
| @@ -56,7 +34,7 @@ | |||||||
|                 (object-at (dec 2 this.menuLeaves.length) this.lastMenuLeaves) |                 (object-at (dec 2 this.menuLeaves.length) this.lastMenuLeaves) | ||||||
|                 this.auth.authData.userRootNamespace |                 this.auth.authData.userRootNamespace | ||||||
|               }} |               }} | ||||||
|                 @class="namespace-link is-current button is-ghost icon" |               @class="namespace-link is-current button is-transparent icon" | ||||||
|             > |             > | ||||||
|               <Chevron @direction="left" @class="has-text-grey" /> |               <Chevron @direction="left" @class="has-text-grey" /> | ||||||
|             </NamespaceLink> |             </NamespaceLink> | ||||||
| @@ -66,12 +44,13 @@ | |||||||
|         {{#if (includes "" this.lastMenuLeaves)}} |         {{#if (includes "" this.lastMenuLeaves)}} | ||||||
|           {{! leaf is '' which is the root namespace, and then we need to iterate the root leaves }} |           {{! leaf is '' which is the root namespace, and then we need to iterate the root leaves }} | ||||||
|           <div class="leaf-panel {{if (eq '' this.currentLeaf) 'leaf-panel-current' 'leaf-panel-left'}} "> |           <div class="leaf-panel {{if (eq '' this.currentLeaf) 'leaf-panel-current' 'leaf-panel-left'}} "> | ||||||
|               {{~#each this.rootLeaves as |rootLeaf|}} |             {{#each this.rootLeaves as |rootLeaf|}} | ||||||
|               <NamespaceLink @targetNamespace={{rootLeaf}} @class="namespace-link" @showLastSegment={{true}} /> |               <NamespaceLink @targetNamespace={{rootLeaf}} @class="namespace-link" @showLastSegment={{true}} /> | ||||||
|               {{/each~}} |             {{/each}} | ||||||
|           </div> |           </div> | ||||||
|         {{/if}} |         {{/if}} | ||||||
|         {{#each this.lastMenuLeaves as |leaf|}} |         {{#each this.lastMenuLeaves as |leaf|}} | ||||||
|  |           {{#if leaf}} | ||||||
|             <div |             <div | ||||||
|               class="leaf-panel |               class="leaf-panel | ||||||
|                 {{if (eq leaf this.currentLeaf) 'leaf-panel-current' 'leaf-panel-left'}} |                 {{if (eq leaf this.currentLeaf) 'leaf-panel-current' 'leaf-panel-left'}} | ||||||
| @@ -79,32 +58,38 @@ | |||||||
|                 {{if (and (not this.isAdding) (eq leaf this.changedLeaf)) 'leaf-panel-exiting'}} |                 {{if (and (not this.isAdding) (eq leaf this.changedLeaf)) 'leaf-panel-exiting'}} | ||||||
|                 " |                 " | ||||||
|             > |             > | ||||||
|               {{~#each-in (get this.namespaceTree leaf) as |leafName|}} |               {{#each-in (get this.namespaceTree leaf) as |leafName|}} | ||||||
|                 <NamespaceLink |                 <NamespaceLink | ||||||
|                   @targetNamespace={{concat leaf "/" leafName}} |                   @targetNamespace={{concat leaf "/" leafName}} | ||||||
|                   @class="namespace-link" |                   @class="namespace-link" | ||||||
|                   @showLastSegment={{true}} |                   @showLastSegment={{true}} | ||||||
|                 /> |                 /> | ||||||
|               {{/each-in~}} |               {{/each-in}} | ||||||
|             </div> |             </div> | ||||||
|  |           {{/if}} | ||||||
|         {{/each}} |         {{/each}} | ||||||
|         {{#if this.canList}} |         {{#if this.canList}} | ||||||
|           <div class="leaf-panel leaf-panel-current"> |           <div class="leaf-panel leaf-panel-current"> | ||||||
|  |             <div class="level"> | ||||||
|  |               <span class="level-left"> | ||||||
|                 <LinkTo @route="vault.cluster.access.namespaces" class="is-block namespace-link namespace-manage-link"> |                 <LinkTo @route="vault.cluster.access.namespaces" class="is-block namespace-link namespace-manage-link"> | ||||||
|                 <div class="level is-mobile"> |                   Manage Namespaces | ||||||
|                   <span class="level-left">Manage namespaces</span> |                 </LinkTo> | ||||||
|  |               </span> | ||||||
|               <span class="level-right"> |               <span class="level-right"> | ||||||
|                     <button type="button" class="button is-ghost icon" onclick={{action "refreshNamespaceList"}}> |                 <button | ||||||
|  |                   type="button" | ||||||
|  |                   class="button is-ghost icon has-right-margin-m" | ||||||
|  |                   data-test-refresh-namespaces | ||||||
|  |                   onclick={{action "refreshNamespaceList"}} | ||||||
|  |                 > | ||||||
|                   <Icon @name="reload" class="has-text-grey" /> |                   <Icon @name="reload" class="has-text-grey" /> | ||||||
|                 </button> |                 </button> | ||||||
|               </span> |               </span> | ||||||
|             </div> |             </div> | ||||||
|               </LinkTo> |  | ||||||
|           </div> |           </div> | ||||||
|         {{/if}} |         {{/if}} | ||||||
|       </div> |       </div> | ||||||
|     </D.Content> |     </D.Content> | ||||||
|   </BasicDropdown> |   </BasicDropdown> | ||||||
|   </div> | </div> | ||||||
|   <div class="navbar-separator"></div> |  | ||||||
| {{/if}} |  | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| <nav class="navbar"> |  | ||||||
|   <LinkStatus @status={{this.currentCluster.cluster.hcpLinkStatus}} /> |  | ||||||
|  |  | ||||||
|   <div class="navbar-actions"> |  | ||||||
|     <div class="navbar-brand" data-test-navheader-home> |  | ||||||
|       {{yield (hash home=(component "nav-header/home"))}} |  | ||||||
|     </div> |  | ||||||
|  |  | ||||||
|     {{#unless this.navDrawerOpen}} |  | ||||||
|       <button type="button" class="navbar-drawer-toggle is-hidden-tablet" {{action "toggleNavDrawer"}}> |  | ||||||
|         <Icon @name="more-vertical" /> |  | ||||||
|         Menu |  | ||||||
|       </button> |  | ||||||
|     {{/unless}} |  | ||||||
|  |  | ||||||
|     {{#unless this.hideLinks}} |  | ||||||
|       <div class="navbar-drawer{{if this.navDrawerOpen ' is-active'}}"> |  | ||||||
|         <div class="navbar-drawer-scroll"> |  | ||||||
|           <div data-test-navheader-main> |  | ||||||
|             {{yield (hash main=(component "nav-header/main") closeDrawer=(action "toggleNavDrawer" false))}} |  | ||||||
|           </div> |  | ||||||
|           <div class="navbar-end" data-test-navheader-items> |  | ||||||
|             {{yield (hash items=(component "nav-header/items") closeDrawer=(action "toggleNavDrawer" false))}} |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         {{#if this.navDrawerOpen}} |  | ||||||
|           <button class="navbar-drawer-toggle is-hidden-tablet" type="button" {{action "toggleNavDrawer" false}}> |  | ||||||
|             <Icon @name="x" /> |  | ||||||
|           </button> |  | ||||||
|         {{/if}} |  | ||||||
|       </div> |  | ||||||
|     {{/unless}} |  | ||||||
|  |  | ||||||
|     <div |  | ||||||
|       class="navbar-drawer-overlay{{if this.navDrawerOpen ' is-active'}}" |  | ||||||
|       role="button" |  | ||||||
|       onclick={{action "toggleNavDrawer" (not this.navDrawerOpen)}} |  | ||||||
|     ></div> |  | ||||||
|   </div> |  | ||||||
| </nav> |  | ||||||
| <Console::UiPanel @isFullscreen={{this.consoleFullscreen}} /> |  | ||||||
| @@ -8,6 +8,9 @@ | |||||||
|   </PageHeader> |   </PageHeader> | ||||||
|   <div class="box is-sideless has-background-white-bis has-text-grey has-text-centered"> |   <div class="box is-sideless has-background-white-bis has-text-grey has-text-centered"> | ||||||
|     <p>Sorry, we were unable to find any content at <code>{{or this.model.path this.path}}</code>.</p> |     <p>Sorry, we were unable to find any content at <code>{{or this.model.path this.path}}</code>.</p> | ||||||
|     <p>Double check the url or go back <HomeLink @text="home" />.</p> |     <p> | ||||||
|  |       Double check the url or | ||||||
|  |       <ExternalLink @href="/" @sameTab={{true}}>go back home</ExternalLink>. | ||||||
|  |     </p> | ||||||
|   </div> |   </div> | ||||||
| </div> | </div> | ||||||
| @@ -1,17 +1,3 @@ | |||||||
| {{#if this.showTruncatedNavBar}} |  | ||||||
|   <NavHeader as |Nav|> |  | ||||||
|     <Nav.home> |  | ||||||
|       <HomeLink @class="navbar-item splash-page-logo has-text-white"> |  | ||||||
|         <LogoEdition /> |  | ||||||
|       </HomeLink> |  | ||||||
|     </Nav.home> |  | ||||||
|     <Nav.items> |  | ||||||
|       <div class="navbar-item status-indicator-button" data-status={{if this.activeCluster.unsealed "good" "bad"}}> |  | ||||||
|         <StatusMenu @label="Status" @onLinkClick={{Nav.closeDrawer}} /> |  | ||||||
|       </div> |  | ||||||
|     </Nav.items> |  | ||||||
|   </NavHeader> |  | ||||||
| {{/if}} |  | ||||||
| {{! bypass container styling }} | {{! bypass container styling }} | ||||||
| {{#if @hasAltContent}} | {{#if @hasAltContent}} | ||||||
|   {{yield (hash altContent=(component "splash-page/splash-content"))}} |   {{yield (hash altContent=(component "splash-page/splash-content"))}} | ||||||
|   | |||||||
| @@ -1,23 +0,0 @@ | |||||||
| <BasicDropdown @horizontalPosition="auto-left" @verticalPosition="below" @renderInPlace={{this.media.isMobile}} as |d|> |  | ||||||
|   <d.Trigger |  | ||||||
|     @htmlTag={{if (eq this.type "replication") "span" "button"}} |  | ||||||
|     class={{if (eq this.type "replication") "" "button is-transparent"}} |  | ||||||
|   > |  | ||||||
|     <span class="status-indicator-color"> |  | ||||||
|       <Icon @name={{this.glyphName}} aria-label={{@ariaLabel}} /> |  | ||||||
|     </span> |  | ||||||
|     <div class="status-menu-label"> |  | ||||||
|       {{@label}} |  | ||||||
|     </div> |  | ||||||
|     <Chevron @direction="down" class="has-text-white is-status-chevron" /> |  | ||||||
|   </d.Trigger> |  | ||||||
|   <d.Content @defaultClass={{concat "status-menu-content status-menu-content-" this.type}}> |  | ||||||
|     {{#if (eq this.type "user")}} |  | ||||||
|       {{#if (and this.currentCluster.cluster.name this.auth.currentToken)}} |  | ||||||
|         <AuthInfo @activeClusterName={{this.currentCluster.cluster.name}} @onLinkClick={{fn this.onLinkClick null}} /> |  | ||||||
|       {{/if}} |  | ||||||
|     {{else}} |  | ||||||
|       <ClusterInfo @cluster={{this.currentCluster.cluster}} @onLinkClick={{fn this.onLinkClick d}} /> |  | ||||||
|     {{/if}} |  | ||||||
|   </d.Content> |  | ||||||
| </BasicDropdown> |  | ||||||
| @@ -7,9 +7,9 @@ | |||||||
|     HashiCorp |     HashiCorp | ||||||
|   </span> |   </span> | ||||||
|   <span> |   <span> | ||||||
|     <ExternalLink @href={{changelog-url-for this.activeCluster.leaderNode.version}} class="link has-text-grey"> |     <ExternalLink @href={{changelog-url-for this.auth.activeCluster.leaderNode.version}} class="link has-text-grey"> | ||||||
|       Vault |       Vault | ||||||
|       {{this.activeCluster.leaderNode.version}} |       {{this.auth.activeCluster.leaderNode.version}} | ||||||
|     </ExternalLink> |     </ExternalLink> | ||||||
|   </span> |   </span> | ||||||
|   {{#if (is-version "OSS")}} |   {{#if (is-version "OSS")}} | ||||||
| @@ -34,4 +34,3 @@ | |||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| {{/if}} | {{/if}} | ||||||
| <div id="modal-wormhole"></div> |  | ||||||
| @@ -1,113 +1,5 @@ | |||||||
| {{#if this.showNav}} | <Sidebar::Nav::Cluster /> | ||||||
|   <NavHeader data-test-header-with-nav @class={{if this.consoleOpen "panel-open"}} as |Nav|> |  | ||||||
|     <Nav.home> |  | ||||||
|       <HomeLink @class="navbar-item has-text-white has-current-color-fill"> |  | ||||||
|         <Icon @name="vault-logo" /> |  | ||||||
|       </HomeLink> |  | ||||||
|     </Nav.home> |  | ||||||
|     <Nav.main> |  | ||||||
|       <ul class="navbar-sections {{if (has-feature 'Namespaces') 'with-ns-picker'}}"> |  | ||||||
|         {{#if (has-feature "Namespaces")}} |  | ||||||
|           <li> |  | ||||||
|             <NamespacePicker @class="navbar-item" @namespace={{this.namespaceQueryParam}} /> |  | ||||||
|           </li> |  | ||||||
|         {{/if}} |  | ||||||
|         <li> |  | ||||||
|           <LinkTo |  | ||||||
|             @route="vault.cluster.secrets" |  | ||||||
|             @current-when="vault.cluster.secrets vault.cluster.settings.mount-secret-backend vault.cluster.settings.configure-secret-backend" |  | ||||||
|             class={{if (is-active-route "vault.cluster.secrets") "is-active"}} |  | ||||||
|             {{on "click" Nav.closeDrawer}} |  | ||||||
|             data-test-navbar-item="secrets" |  | ||||||
|           > |  | ||||||
|             Secrets |  | ||||||
|           </LinkTo> |  | ||||||
|         </li> |  | ||||||
|         {{#if (has-permission "access")}} |  | ||||||
|           <li> |  | ||||||
|             <LinkTo |  | ||||||
|               @route={{get (route-params-for "access") "route"}} |  | ||||||
|               @models={{get (route-params-for "access") "models"}} |  | ||||||
|               @current-when="vault.cluster.access vault.cluster.settings.auth" |  | ||||||
|               class={{if (is-active-route "vault.cluster.access") "is-active"}} |  | ||||||
|               {{on "click" Nav.closeDrawer}} |  | ||||||
|               data-test-navbar-item="access" |  | ||||||
|             > |  | ||||||
|               Access |  | ||||||
|             </LinkTo> |  | ||||||
|           </li> |  | ||||||
|         {{/if}} |  | ||||||
|         {{#if (has-permission "policies")}} |  | ||||||
|           <li> |  | ||||||
|             <LinkTo |  | ||||||
|               @route="vault.cluster.policies" |  | ||||||
|               @models={{get (route-params-for "policies") "models"}} |  | ||||||
|               @current-when="vault.cluster.policies vault.cluster.policy" |  | ||||||
|               class={{if (is-active-route (array "vault.cluster.policies" "vault.cluster.policy")) "is-active"}} |  | ||||||
|               {{on "click" Nav.closeDrawer}} |  | ||||||
|               data-test-navbar-item="policies" |  | ||||||
|             > |  | ||||||
|               Policies |  | ||||||
|             </LinkTo> |  | ||||||
|           </li> |  | ||||||
|         {{/if}} |  | ||||||
|         {{#if (has-permission "tools")}} |  | ||||||
|           <li> |  | ||||||
|             <LinkTo |  | ||||||
|               @route="vault.cluster.tools.tool" |  | ||||||
|               @models={{get (route-params-for "tools") "models"}} |  | ||||||
|               class={{if (is-active-route "vault.cluster.tools") "is-active"}} |  | ||||||
|               {{on "click" Nav.closeDrawer}} |  | ||||||
|             > |  | ||||||
|               Tools |  | ||||||
|             </LinkTo> |  | ||||||
|           </li> |  | ||||||
|         {{/if}} |  | ||||||
|       </ul> |  | ||||||
|     </Nav.main> |  | ||||||
|     <Nav.items> |  | ||||||
|       <div class="navbar-separator is-hidden-tablet"></div> |  | ||||||
|       {{! template-lint-disable block-indentation }} |  | ||||||
|       {{#if this.namespaceService.inRootNamespace}} |  | ||||||
|         <div class="navbar-item status-indicator-button" data-status={{if this.activeCluster.unsealed "good" "bad"}}> |  | ||||||
|           <StatusMenu @label="Status" @onLinkClick={{action Nav.closeDrawer}} /> |  | ||||||
|         </div> |  | ||||||
|         <div class="navbar-separator is-hidden-mobile"></div> |  | ||||||
|       {{else if (and |  | ||||||
|         (has-permission "clients" routeParams="activity") (not this.cluster.dr.isSecondary) this.auth.currentToken |  | ||||||
|       )}} |  | ||||||
|         <div class="navbar-sections"> |  | ||||||
|           <div class={{if (is-active-route "vault.cluster.clients") "is-active"}}> |  | ||||||
|             <LinkTo @route="vault.cluster.clients.dashboard" data-test-navbar-item="metrics"> |  | ||||||
|               Client count |  | ||||||
|             </LinkTo> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|       {{/if}} |  | ||||||
|       {{! template-lint-enable block-indentation }} |  | ||||||
|       <div class="navbar-item"> |  | ||||||
|         <button |  | ||||||
|           type="button" |  | ||||||
|           class="button is-transparent nav-console-button{{if this.consoleOpen ' popup-open'}}" |  | ||||||
|           {{action (queue (action "toggleConsole") (action Nav.closeDrawer))}} |  | ||||||
|           data-test-console-toggle |  | ||||||
|         > |  | ||||||
|           <Icon @name="terminal-screen" /> |  | ||||||
|           <div class="status-menu-label"> |  | ||||||
|             Console |  | ||||||
|           </div> |  | ||||||
|           <Chevron @direction="down" class="has-text-white is-status-chevron" /> |  | ||||||
|         </button> |  | ||||||
|       </div> |  | ||||||
|       <div |  | ||||||
|         class="navbar-item nav-user-button {{if this.auth.allowExpiration 'may-expire'}}" |  | ||||||
|         data-test-allow-expiration={{this.auth.allowExpiration}} |  | ||||||
|       > |  | ||||||
|         <StatusMenu @type="user" @label="User" @onLinkClick={{action Nav.closeDrawer}} /> |  | ||||||
|       </div> |  | ||||||
|     </Nav.items> |  | ||||||
|   </NavHeader> |  | ||||||
| {{/if}} |  | ||||||
| <LicenseBanners | <LicenseBanners | ||||||
|   @expiry={{this.activeCluster.licenseExpiry}} |   @expiry={{this.activeCluster.licenseExpiry}} | ||||||
|   @autoloaded={{eq this.activeCluster.licenseState "autoloaded"}} |   @autoloaded={{eq this.activeCluster.licenseState "autoloaded"}} | ||||||
| @@ -128,10 +20,8 @@ | |||||||
|     </FlashMessage> |     </FlashMessage> | ||||||
|   {{/each}} |   {{/each}} | ||||||
| </div> | </div> | ||||||
| {{#if this.currentlyLoading}} |  | ||||||
|   <LogoSplash /> | {{#if this.auth.isActiveSession}} | ||||||
| {{else}} |  | ||||||
|   {{#if this.showNav}} |  | ||||||
|   <section class="section"> |   <section class="section"> | ||||||
|     <div class="container is-widescreen"> |     <div class="container is-widescreen"> | ||||||
|       <TokenExpireWarning @expirationDate={{this.auth.tokenExpirationDate}}> |       <TokenExpireWarning @expirationDate={{this.auth.tokenExpirationDate}}> | ||||||
| @@ -139,7 +29,6 @@ | |||||||
|       </TokenExpireWarning> |       </TokenExpireWarning> | ||||||
|     </div> |     </div> | ||||||
|   </section> |   </section> | ||||||
|   {{else}} | {{else}} | ||||||
|   {{outlet}} |   {{outlet}} | ||||||
|   {{/if}} |  | ||||||
| {{/if}} | {{/if}} | ||||||
| @@ -1,75 +1,2 @@ | |||||||
| <div class="columns"> | <Sidebar::Nav::Access /> | ||||||
|   <MenuSidebar @title="Access" @class="is-3" @data-test-sidebar={{true}}> | {{outlet}} | ||||||
|     {{#if (has-permission "access" routeParams="methods")}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo |  | ||||||
|           @route="vault.cluster.access.methods" |  | ||||||
|           data-test-link={{true}} |  | ||||||
|           @current-when="vault.cluster.access.methods vault.cluster.access.method" |  | ||||||
|         > |  | ||||||
|           Auth Methods |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|     {{#if (has-permission "access" routeParams="mfa")}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo |  | ||||||
|           @route="vault.cluster.access.mfa.methods" |  | ||||||
|           @current-when="vault.cluster.access.mfa.methods vault.cluster.access.mfa.enforcements vault.cluster.access.mfa.index" |  | ||||||
|           data-test-link="mfa" |  | ||||||
|         > |  | ||||||
|           Multi-factor authentication |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|     {{#if (has-permission "access" routeParams="entities")}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo @route="vault.cluster.access.identity" @model="entities" data-test-link={{true}}> |  | ||||||
|           Entities |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|     {{#if (has-permission "access" routeParams="groups")}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo @route="vault.cluster.access.identity" @model="groups" data-test-link={{true}}> |  | ||||||
|           Groups |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|     {{#if (has-permission "access" routeParams="leases")}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo @route="vault.cluster.access.leases" data-test-link={{true}}> |  | ||||||
|           Leases |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|     {{#if (and (has-feature "Namespaces") (has-permission "access" routeParams="namespaces"))}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo @route="vault.cluster.access.namespaces" data-test-link={{true}}> |  | ||||||
|           Namespaces |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|     {{#if (and (has-feature "Control Groups") (has-permission "access" routeParams="control-groups"))}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo |  | ||||||
|           @route="vault.cluster.access.control-groups" |  | ||||||
|           data-test-link={{true}} |  | ||||||
|           @current-when="vault.cluster.access.control-groups vault.cluster.access.control-group-accessor vault.cluster.access.control-groups-configure" |  | ||||||
|         > |  | ||||||
|           Control Groups |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|     {{#if (has-permission "access" routeParams="oidc")}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo @route="vault.cluster.access.oidc" data-test-link="oidc"> |  | ||||||
|           OIDC Provider |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|   </MenuSidebar> |  | ||||||
|   <div class="column is-9"> |  | ||||||
|     {{outlet}} |  | ||||||
|   </div> |  | ||||||
| </div> |  | ||||||
							
								
								
									
										1
									
								
								ui/app/templates/vault/cluster/access/loading.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ui/app/templates/vault/cluster/access/loading.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <LayoutLoading /> | ||||||
| @@ -22,6 +22,11 @@ | |||||||
|         <LogoEdition aria-label="Sign in with Hashicorp Vault" /> |         <LogoEdition aria-label="Sign in with Hashicorp Vault" /> | ||||||
|       </div> |       </div> | ||||||
|     {{else}} |     {{else}} | ||||||
|  |       <div class="is-flex-v-centered has-bottom-margin-xxl"> | ||||||
|  |         <div class="brand-icon-large"> | ||||||
|  |           <Icon @name="vault" @size="24" @stretched={{true}} /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|       <div class="is-flex-row"> |       <div class="is-flex-row"> | ||||||
|         {{#if this.mfaAuthData}} |         {{#if this.mfaAuthData}} | ||||||
|           <button type="button" class="icon-button" {{on "click" (fn (mut this.mfaAuthData) null)}}> |           <button type="button" class="icon-button" {{on "click" (fn (mut this.mfaAuthData) null)}}> | ||||||
|   | |||||||
| @@ -23,8 +23,5 @@ | |||||||
|     </ul> |     </ul> | ||||||
|   </nav> |   </nav> | ||||||
| </div> | </div> | ||||||
| {{#if this.currentlyLoading}} |  | ||||||
|   <LayoutLoading /> | {{outlet}} | ||||||
| {{else}} |  | ||||||
|   {{outlet}} |  | ||||||
| {{/if}} |  | ||||||
							
								
								
									
										1
									
								
								ui/app/templates/vault/cluster/clients/loading.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ui/app/templates/vault/cluster/clients/loading.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <LayoutLoading /> | ||||||
| @@ -1,51 +1,2 @@ | |||||||
| {{#if | <Sidebar::Nav::Policies /> | ||||||
|   (and | {{outlet}} | ||||||
|     (has-feature "Sentinel") (or (has-permission "policies" routeParams="rgp") (has-permission "policies" routeParams="egp")) |  | ||||||
|   ) |  | ||||||
| }} |  | ||||||
|   <div class="columns"> |  | ||||||
|     <MenuSidebar @title="Policies" @class="is-3" @data-test-sidebar={{true}}> |  | ||||||
|       {{#if (has-permission "policies" routeParams="acl")}} |  | ||||||
|         <li> |  | ||||||
|           <LinkTo |  | ||||||
|             @route="vault.cluster.policies" |  | ||||||
|             @model="acl" |  | ||||||
|             data-test-link={{true}} |  | ||||||
|             class={{if (is-active-route "vault.cluster.policies" "acl") "is-active"}} |  | ||||||
|           > |  | ||||||
|             ACL Policies |  | ||||||
|           </LinkTo> |  | ||||||
|         </li> |  | ||||||
|       {{/if}} |  | ||||||
|       {{#if (has-permission "policies" routeParams="rgp")}} |  | ||||||
|         <li> |  | ||||||
|           <LinkTo |  | ||||||
|             @route="vault.cluster.policies" |  | ||||||
|             @model="rgp" |  | ||||||
|             data-test-link={{true}} |  | ||||||
|             class={{if (is-active-route "vault.cluster.policies" "rgp") "is-active"}} |  | ||||||
|           > |  | ||||||
|             Role Governing Policies |  | ||||||
|           </LinkTo> |  | ||||||
|         </li> |  | ||||||
|       {{/if}} |  | ||||||
|       {{#if (has-permission "policies" routeParams="egp")}} |  | ||||||
|         <li> |  | ||||||
|           <LinkTo |  | ||||||
|             @route="vault.cluster.policies" |  | ||||||
|             @model="egp" |  | ||||||
|             data-test-link={{true}} |  | ||||||
|             class={{if (is-active-route "vault.cluster.policies" "egp") "is-active"}} |  | ||||||
|           > |  | ||||||
|             Endpoint Governing Policies |  | ||||||
|           </LinkTo> |  | ||||||
|         </li> |  | ||||||
|       {{/if}} |  | ||||||
|     </MenuSidebar> |  | ||||||
|     <div class="column is-9"> |  | ||||||
|       {{outlet}} |  | ||||||
|     </div> |  | ||||||
|   </div> |  | ||||||
| {{else}} |  | ||||||
|   {{outlet}} |  | ||||||
| {{/if}} |  | ||||||
| @@ -1,45 +1,2 @@ | |||||||
| <div class="columns"> | <Sidebar::Nav::Policies /> | ||||||
|   <MenuSidebar @title="Policies" @class="is-3" @data-test-sidebar={{true}}> | {{outlet}} | ||||||
|     {{#if (has-permission "policies" routeParams="acl")}} |  | ||||||
|       <li> |  | ||||||
|         <LinkTo |  | ||||||
|           @route="vault.cluster.policies" |  | ||||||
|           @model="acl" |  | ||||||
|           data-test-link={{true}} |  | ||||||
|           class={{if (is-active-route "vault.cluster.policy" "acl") "is-active"}} |  | ||||||
|         > |  | ||||||
|           ACL Policies |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|     {{/if}} |  | ||||||
|     {{#if (has-feature "Sentinel")}} |  | ||||||
|       {{#if (has-permission "policies" routeParams="rgp")}} |  | ||||||
|         <li> |  | ||||||
|           <LinkTo |  | ||||||
|             @route="vault.cluster.policies" |  | ||||||
|             @model="rgp" |  | ||||||
|             data-test-link={{true}} |  | ||||||
|             class={{if (is-active-route "vault.cluster.policy" "rgp") "is-active"}} |  | ||||||
|           > |  | ||||||
|             Role Governing Policies |  | ||||||
|           </LinkTo> |  | ||||||
|         </li> |  | ||||||
|       {{/if}} |  | ||||||
|       {{#if (has-permission "policies" routeParams="egp")}} |  | ||||||
|         <li> |  | ||||||
|           <LinkTo |  | ||||||
|             @route="vault.cluster.policies" |  | ||||||
|             @model="egp" |  | ||||||
|             data-test-link={{true}} |  | ||||||
|             class={{if (is-active-route "vault.cluster.policy" "egp") "is-active"}} |  | ||||||
|           > |  | ||||||
|             Endpoint Governing Policies |  | ||||||
|           </LinkTo> |  | ||||||
|         </li> |  | ||||||
|       {{/if}} |  | ||||||
|     {{/if}} |  | ||||||
|   </MenuSidebar> |  | ||||||
|   <div class="column is-9"> |  | ||||||
|     {{outlet}} |  | ||||||
|   </div> |  | ||||||
| </div> |  | ||||||
| @@ -56,7 +56,7 @@ | |||||||
|           {{#if this.model.secret}} |           {{#if this.model.secret}} | ||||||
|             <LinkTo @route="vault.cluster.secrets.backend.list-root">Navigate back to the root</LinkTo>. |             <LinkTo @route="vault.cluster.secrets.backend.list-root">Navigate back to the root</LinkTo>. | ||||||
|           {{else}} |           {{else}} | ||||||
|             <HomeLink>Go back home</HomeLink>. |             <LinkTo @route="vault.cluster">Go back home</LinkTo>. | ||||||
|           {{/if}} |           {{/if}} | ||||||
|         </p> |         </p> | ||||||
|       {{/if}} |       {{/if}} | ||||||
|   | |||||||
| @@ -1,21 +1,2 @@ | |||||||
| <div class="columns"> | <Sidebar::Nav::Tools /> | ||||||
|   <MenuSidebar @title="Tools" @class="is-3"> | <ToolActionsForm @selectedAction={{this.selectedAction}} /> | ||||||
|     {{#each (tools-actions) as |supportedAction|}} |  | ||||||
|       {{#if (has-permission "tools" routeParams=supportedAction)}} |  | ||||||
|         <li> |  | ||||||
|           <LinkTo |  | ||||||
|             @route="vault.cluster.tools.tool" |  | ||||||
|             @model={{supportedAction}} |  | ||||||
|             class={{if (eq supportedAction this.selectedAction) "is-active"}} |  | ||||||
|             data-test-tools-action-link={{supportedAction}} |  | ||||||
|           > |  | ||||||
|             {{capitalize supportedAction}} |  | ||||||
|           </LinkTo> |  | ||||||
|         </li> |  | ||||||
|       {{/if}} |  | ||||||
|     {{/each}} |  | ||||||
|   </MenuSidebar> |  | ||||||
|   <div class="column is-9"> |  | ||||||
|     <ToolActionsForm @selectedAction={{this.selectedAction}} /> |  | ||||||
|   </div> |  | ||||||
| </div> |  | ||||||
| @@ -1,16 +1,4 @@ | |||||||
| {{#if this.showLicenseError}} | {{#if this.showLicenseError}} | ||||||
|   <NavHeader as |Nav|> |  | ||||||
|     <Nav.home> |  | ||||||
|       <HomeLink @class="navbar-item splash-page-logo has-text-white"> |  | ||||||
|         <LogoEdition /> |  | ||||||
|       </HomeLink> |  | ||||||
|     </Nav.home> |  | ||||||
|     <Nav.items> |  | ||||||
|       <div class="navbar-item status-indicator-button" data-status={{if this.activeCluster.unsealed "good" "bad"}}> |  | ||||||
|         <StatusMenu @label="Status" @onLinkClick={{action Nav.closeDrawer}} /> |  | ||||||
|       </div> |  | ||||||
|     </Nav.items> |  | ||||||
|   </NavHeader> |  | ||||||
|   <div class="section is-flex-v-centered-tablet is-flex-1 is-fullwidth"> |   <div class="section is-flex-v-centered-tablet is-flex-1 is-fullwidth"> | ||||||
|     <div class="columns is-centered is-gapless is-fullwidth"> |     <div class="columns is-centered is-gapless is-fullwidth"> | ||||||
|       <EmptyState |       <EmptyState | ||||||
|   | |||||||
| @@ -1,34 +1,45 @@ | |||||||
| <NavHeader data-test-header-without-nav as |Nav|> | <div class="is-flex-1 is-flex-v-centered"> | ||||||
|   <Nav.home> |   <div class="empty-state-content"> | ||||||
|     <HomeLink @class="navbar-item splash-page-logo"> |     <div class="is-flex-v-centered has-bottom-margin-xxl"> | ||||||
|       <LogoEdition /> |       <div class="brand-icon-large"> | ||||||
|     </HomeLink> |         <Icon @name="vault" @size="24" @stretched={{true}} /> | ||||||
|   </Nav.home> |       </div> | ||||||
| </NavHeader> |     </div> | ||||||
| <section class="section"> |     <div class="is-flex-center"> | ||||||
|   <div class="container"> |       <div class="error-icon"> | ||||||
|     {{#if (eq this.model.httpStatus 404)}} |         <Icon @name="help" @size="24" class="has-text-grey" @stretched={{true}} /> | ||||||
|       <NotFound @model={{this.model}} /> |       </div> | ||||||
|     {{else}} |       <div class="has-left-margin-s"> | ||||||
|       <PageHeader as |p|> |         <h1 class="is-size-4 has-text-semibold has-text-grey has-line-height-1"> | ||||||
|         <p.levelLeft> |  | ||||||
|           <h1 class="title is-3 has-text-grey"> |  | ||||||
|           {{#if (eq this.model.httpStatus 403)}} |           {{#if (eq this.model.httpStatus 403)}} | ||||||
|             Not authorized |             Not authorized | ||||||
|  |           {{else if (eq this.model.httpStatus 404)}} | ||||||
|  |             Page not found | ||||||
|           {{else}} |           {{else}} | ||||||
|             Error |             Error | ||||||
|           {{/if}} |           {{/if}} | ||||||
|         </h1> |         </h1> | ||||||
|         </p.levelLeft> |         <p class="has-text-grey is-size-8">Error {{this.model.httpStatus}}</p> | ||||||
|       </PageHeader> |  | ||||||
|       <BlockError> |  | ||||||
|         {{#if this.model.message}} |  | ||||||
|           <p>{{this.model.message}}</p> |  | ||||||
|         {{/if}} |  | ||||||
|         {{#each this.model.errors as |error|}} |  | ||||||
|           <p>{{error}}</p> |  | ||||||
|         {{/each}} |  | ||||||
|       </BlockError> |  | ||||||
|     {{/if}} |  | ||||||
|       </div> |       </div> | ||||||
| </section> |     </div> | ||||||
|  |  | ||||||
|  |     <p class="has-text-grey has-top-margin-m has-bottom-padding-l has-border-bottom-light" data-test-error-description> | ||||||
|  |       {{#if (eq this.model.httpStatus 404)}} | ||||||
|  |         Sorry, we were unable to find any content at that URL. Double check it or go back home. | ||||||
|  |       {{else}} | ||||||
|  |         {{this.model.message}} | ||||||
|  |         {{join ". " this.model.errors}} | ||||||
|  |       {{/if}} | ||||||
|  |     </p> | ||||||
|  |  | ||||||
|  |     <div class="is-flex-between has-top-margin-s"> | ||||||
|  |       <ExternalLink @href="/" @sameTab={{true}} class="is-no-underline is-flex-center has-text-semibold"> | ||||||
|  |         <Chevron @direction="left" /> | ||||||
|  |         Go home | ||||||
|  |       </ExternalLink> | ||||||
|  |       <DocLink @path="/vault/api-docs#http-status-codes"> | ||||||
|  |         Learn more | ||||||
|  |       </DocLink> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
| @@ -1,14 +1 @@ | |||||||
| <header> | <NotFound @model={{this.model}} /> | ||||||
|   <nav class="navbar is-grouped-split"> |  | ||||||
|     <div class="navbar-brand"> |  | ||||||
|       <HomeLink @class="navbar-item has-text-white has-current-color-fill"> |  | ||||||
|         <Icon @name="vault-logo" /> |  | ||||||
|       </HomeLink> |  | ||||||
|     </div> |  | ||||||
|   </nav> |  | ||||||
| </header> |  | ||||||
| <section class="section"> |  | ||||||
|   <div class="container is-widescreen"> |  | ||||||
|     <NotFound @model={{this.model}} /> |  | ||||||
|   </div> |  | ||||||
| </section> |  | ||||||
| @@ -8,7 +8,6 @@ | |||||||
|  |  | ||||||
| const EmberApp = require('ember-cli/lib/broccoli/ember-app'); | const EmberApp = require('ember-cli/lib/broccoli/ember-app'); | ||||||
| const config = require('./config/environment')(); | const config = require('./config/environment')(); | ||||||
| const nodeSass = require('node-sass'); |  | ||||||
|  |  | ||||||
| const environment = EmberApp.env(); | const environment = EmberApp.env(); | ||||||
| const isProd = environment === 'production'; | const isProd = environment === 'production'; | ||||||
| @@ -44,9 +43,18 @@ const appConfig = { | |||||||
|     enabled: !isProd, |     enabled: !isProd, | ||||||
|   }, |   }, | ||||||
|   sassOptions: { |   sassOptions: { | ||||||
|     implementation: nodeSass, |  | ||||||
|     sourceMap: false, |     sourceMap: false, | ||||||
|     onlyIncluded: true, |     onlyIncluded: true, | ||||||
|  |     precision: 4, | ||||||
|  |     includePaths: [ | ||||||
|  |       './node_modules/@hashicorp/design-system-components/app/styles', | ||||||
|  |       './node_modules/@hashicorp/design-system-tokens/dist/products/css', | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   minifyCSS: { | ||||||
|  |     options: { | ||||||
|  |       advanced: false, | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
|   autoprefixer: { |   autoprefixer: { | ||||||
|     enabled: isTest || isProd, |     enabled: isTest || isProd, | ||||||
|   | |||||||
| @@ -1,18 +1,3 @@ | |||||||
| {{! DR Secondary has a different Nav Header with access only to the Status menu }} |  | ||||||
| {{#if this.isSecondary}} |  | ||||||
|   <NavHeader as |Nav|> |  | ||||||
|     <Nav.home> |  | ||||||
|       <HomeLink @class="navbar-item splash-page-logo has-text-white"> |  | ||||||
|         <LogoEdition /> |  | ||||||
|       </HomeLink> |  | ||||||
|     </Nav.home> |  | ||||||
|     <Nav.items> |  | ||||||
|       <div class="navbar-item status-indicator-button" data-status={{if this.data.unsealed "good" "bad"}}> |  | ||||||
|         <StatusMenu @label="Status" @onLinkClick={{action Nav.closeDrawer}} /> |  | ||||||
|       </div> |  | ||||||
|     </Nav.items> |  | ||||||
|   </NavHeader> |  | ||||||
| {{/if}} |  | ||||||
| <PageHeader as |p|> | <PageHeader as |p|> | ||||||
|   <p.top> |   <p.top> | ||||||
|     {{! template-lint-configure simple-unless "warn" }} |     {{! template-lint-configure simple-unless "warn" }} | ||||||
| @@ -55,14 +40,10 @@ | |||||||
|         </ul> |         </ul> | ||||||
|       {{else}} |       {{else}} | ||||||
|         <ul> |         <ul> | ||||||
|           <LinkTo @route="vault.cluster.replication-dr-promote.details" @activeClass="is-active"> |           <LinkTo @route="vault.cluster.replication-dr-promote.details"> | ||||||
|             Details |             Details | ||||||
|           </LinkTo> |           </LinkTo> | ||||||
|           <LinkTo |           <LinkTo @route="vault.cluster.replication-dr-promote" @current-when="vault.cluster.replication-dr-promote.index"> | ||||||
|             @route="vault.cluster.replication-dr-promote" |  | ||||||
|             @activeClass="is-active" |  | ||||||
|             @current-when="vault.cluster.replication-dr-promote.index" |  | ||||||
|           > |  | ||||||
|             Manage |             Manage | ||||||
|           </LinkTo> |           </LinkTo> | ||||||
|         </ul> |         </ul> | ||||||
|   | |||||||
| @@ -98,7 +98,12 @@ | |||||||
|     </div> |     </div> | ||||||
|     <div class="level-right"> |     <div class="level-right"> | ||||||
|       {{#if this.replicationDisabled}} |       {{#if this.replicationDisabled}} | ||||||
|         <LinkTo @route="mode.index" @models={{array this.cluster.name this.mode}} class="button is-primary"> |         <LinkTo | ||||||
|  |           @route="mode.index" | ||||||
|  |           @models={{array this.cluster.name this.mode}} | ||||||
|  |           class="button is-primary" | ||||||
|  |           data-test-replication-promote-secondary | ||||||
|  |         > | ||||||
|           Enable |           Enable | ||||||
|         </LinkTo> |         </LinkTo> | ||||||
|       {{else}} |       {{else}} | ||||||
|   | |||||||
| @@ -21,8 +21,7 @@ export default Route.extend(ClusterRoute, { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   model() { |   model() { | ||||||
|     const activeClusterId = this.auth.activeCluster; |     return this.auth.activeCluster; | ||||||
|     return this.store.peekRecord('cluster', activeClusterId); |  | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   afterModel(model) { |   afterModel(model) { | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| <section class="section"> | <section class="section"> | ||||||
|   <div class="container is-widescreen"> |   <div class="container is-widescreen"> | ||||||
|     {{#if this.model.replicationIsInitializing}} |     {{#if this.model.replicationIsInitializing}} | ||||||
|       <nav class="navbar"></nav> |  | ||||||
|       <LayoutLoading /> |       <LayoutLoading /> | ||||||
|     {{else}} |     {{else}} | ||||||
|       {{#if (eq this.model.mode "unsupported")}} |       {{#if (eq this.model.mode "unsupported")}} | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| <section class="section"> | <section class="section"> | ||||||
|   <div class="container is-widescreen"> |   <div class="container is-widescreen"> | ||||||
|     {{#if (and (eq this.model.drMode "secondary") (eq this.model.drModeInit "primary"))}} |     {{#if (and (eq this.model.drMode "secondary") (eq this.model.drModeInit "primary"))}} | ||||||
|       <nav class="navbar" aria-label="loading nav"></nav> |  | ||||||
|       <LayoutLoading /> |       <LayoutLoading /> | ||||||
|     {{else}} |     {{else}} | ||||||
|       {{#if this.model.replicationAttrs.replicationEnabled}} |       {{#if this.model.replicationAttrs.replicationEnabled}} | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Jordan Reimer
					Jordan Reimer