From f68f87bc01df33d6ef62bb7aa0e14c0b00627ab8 Mon Sep 17 00:00:00 2001 From: Avi Shah Date: Tue, 24 Sep 2024 12:50:33 +0530 Subject: [PATCH] new main product landing page and navs --- app/components/ak-popover/index.hbs | 1 + app/components/ak-popover/index.ts | 1 + app/components/ak-svg/appknox-bg-img.hbs | 37 + app/components/ak-svg/sm-indicator.hbs | 21 + app/components/ak-svg/storeknox-bg-img.hbs | 37 + app/components/ak-svg/vp-indicator.hbs | 21 + app/components/ak-tabs/item/index.scss | 3 +- app/components/appknox-wrapper/index.hbs | 86 ++ .../index.scss | 19 +- .../side-nav => appknox-wrapper}/index.ts | 264 ++++--- .../onboarding-guide/index.hbs | 0 .../onboarding-guide/index.scss | 2 +- .../onboarding-guide/index.ts | 10 +- app/components/file-compare/index.ts | 4 +- app/components/file-compare/table/index.ts | 9 +- .../vulnerability-details/index.ts | 4 +- app/components/home-page/index.hbs | 90 ++- app/components/home-page/index.js | 89 --- app/components/home-page/index.scss | 23 + app/components/home-page/index.ts | 62 ++ .../organization-dashboard/header/index.hbs | 93 --- .../organization-dashboard/header/index.scss | 20 - .../organization-dashboard/index.hbs | 32 - .../home-page/organization-dashboard/index.ts | 58 -- .../side-nav/security-menu-item/index.ts | 23 - .../home-page/product-card/index.hbs | 45 ++ .../home-page/product-card/index.scss | 15 + .../home-page/product-card/index.ts | 32 + .../security-dashboard-nav/index.hbs | 51 -- .../security-dashboard-nav/index.scss | 17 - .../home-page/security-dashboard-nav/index.ts | 82 -- app/components/partner/template.hbs | 50 +- app/components/security-wrapper/index.hbs | 10 + app/components/security-wrapper/index.scss | 3 + app/components/security-wrapper/index.ts | 45 ++ app/components/security/nav-menu/index.hbs | 2 +- app/components/security/nav-menu/index.scss | 10 +- .../side-nav/index.hbs | 42 +- .../side-nav/index.scss | 53 +- app/components/side-nav/index.ts | 130 +++ .../side-nav/product-switcher/index.hbs | 81 ++ .../side-nav/product-switcher/index.scss | 57 ++ .../side-nav/product-switcher/index.ts | 84 ++ .../side-nav/security-menu-item/index.hbs | 0 .../side-nav/security-menu-item/index.ts | 23 + app/components/storeknox-wrapper/index.hbs | 26 + app/components/storeknox-wrapper/index.scss | 38 + app/components/storeknox-wrapper/index.ts | 75 ++ app/components/top-nav/index.hbs | 74 ++ app/components/top-nav/index.scss | 43 + .../header => top-nav}/index.ts | 50 +- app/router.ts | 16 + app/routes/authenticated/dashboard.ts | 3 + app/routes/authenticated/home.ts | 16 + app/routes/authenticated/index.ts | 2 +- app/routes/authenticated/partner.ts | 9 +- app/routes/authenticated/storeknox.ts | 16 + app/services/user-auth.ts | 19 + app/styles/_component-variables.scss | 127 ++- app/styles/_icons.scss | 12 + app/styles/_theme.scss | 2 +- app/templates/authenticated.hbs | 4 +- app/templates/authenticated/dashboard.hbs | 5 + app/templates/authenticated/home.hbs | 3 + app/templates/authenticated/partner.hbs | 8 +- app/templates/authenticated/security.hbs | 6 +- app/templates/authenticated/storeknox.hbs | 5 + .../authenticated/storeknox/discover.hbs | 0 .../authenticated/storeknox/inventory.hbs | 1 + app/utils/scroll-to-top.ts | 12 +- package-lock.json | 740 +++++++----------- tests/acceptance/home-page-test.js | 148 ++++ tests/acceptance/side-nav-test.js | 159 ++++ .../components/appknox-wrapper-test.js | 414 ++++++++++ .../appknox-wrapper/onboarding-guide-test.js | 89 +++ .../organization-dashboard/header-test.js | 255 ------ .../onboarding-guide-test.js | 92 --- .../organization-dashboard/side-nav-test.js | 377 --------- .../home-page/security-dashboard-nav-test.js | 84 -- .../components/security-wrapper-test.js | 106 +++ .../components/storeknox-wrapper-test.js | 166 ++++ tests/integration/components/top-nav-test.js | 161 ++++ translations/en.json | 14 + translations/ja.json | 14 + types/ak-svg.d.ts | 5 + 85 files changed, 3170 insertions(+), 2067 deletions(-) create mode 100644 app/components/ak-svg/appknox-bg-img.hbs create mode 100644 app/components/ak-svg/sm-indicator.hbs create mode 100644 app/components/ak-svg/storeknox-bg-img.hbs create mode 100644 app/components/ak-svg/vp-indicator.hbs create mode 100644 app/components/appknox-wrapper/index.hbs rename app/components/{home-page/organization-dashboard => appknox-wrapper}/index.scss (63%) rename app/components/{home-page/organization-dashboard/side-nav => appknox-wrapper}/index.ts (67%) rename app/components/{home-page/organization-dashboard => appknox-wrapper}/onboarding-guide/index.hbs (100%) rename app/components/{home-page/organization-dashboard => appknox-wrapper}/onboarding-guide/index.scss (81%) rename app/components/{home-page/organization-dashboard => appknox-wrapper}/onboarding-guide/index.ts (88%) delete mode 100644 app/components/home-page/index.js create mode 100644 app/components/home-page/index.scss create mode 100644 app/components/home-page/index.ts delete mode 100644 app/components/home-page/organization-dashboard/header/index.hbs delete mode 100644 app/components/home-page/organization-dashboard/header/index.scss delete mode 100644 app/components/home-page/organization-dashboard/index.hbs delete mode 100644 app/components/home-page/organization-dashboard/index.ts delete mode 100644 app/components/home-page/organization-dashboard/side-nav/security-menu-item/index.ts create mode 100644 app/components/home-page/product-card/index.hbs create mode 100644 app/components/home-page/product-card/index.scss create mode 100644 app/components/home-page/product-card/index.ts delete mode 100644 app/components/home-page/security-dashboard-nav/index.hbs delete mode 100644 app/components/home-page/security-dashboard-nav/index.scss delete mode 100644 app/components/home-page/security-dashboard-nav/index.ts create mode 100644 app/components/security-wrapper/index.hbs create mode 100644 app/components/security-wrapper/index.scss create mode 100644 app/components/security-wrapper/index.ts rename app/components/{home-page/organization-dashboard => }/side-nav/index.hbs (89%) rename app/components/{home-page/organization-dashboard => }/side-nav/index.scss (65%) create mode 100644 app/components/side-nav/index.ts create mode 100644 app/components/side-nav/product-switcher/index.hbs create mode 100644 app/components/side-nav/product-switcher/index.scss create mode 100644 app/components/side-nav/product-switcher/index.ts rename app/components/{home-page/organization-dashboard => }/side-nav/security-menu-item/index.hbs (100%) create mode 100644 app/components/side-nav/security-menu-item/index.ts create mode 100644 app/components/storeknox-wrapper/index.hbs create mode 100644 app/components/storeknox-wrapper/index.scss create mode 100644 app/components/storeknox-wrapper/index.ts create mode 100644 app/components/top-nav/index.hbs create mode 100644 app/components/top-nav/index.scss rename app/components/{home-page/organization-dashboard/header => top-nav}/index.ts (62%) create mode 100644 app/routes/authenticated/dashboard.ts create mode 100644 app/routes/authenticated/home.ts create mode 100644 app/routes/authenticated/storeknox.ts create mode 100644 app/services/user-auth.ts create mode 100644 app/templates/authenticated/dashboard.hbs create mode 100644 app/templates/authenticated/home.hbs create mode 100644 app/templates/authenticated/storeknox.hbs create mode 100644 app/templates/authenticated/storeknox/discover.hbs create mode 100644 app/templates/authenticated/storeknox/inventory.hbs create mode 100644 tests/acceptance/home-page-test.js create mode 100644 tests/acceptance/side-nav-test.js create mode 100644 tests/integration/components/appknox-wrapper-test.js create mode 100644 tests/integration/components/appknox-wrapper/onboarding-guide-test.js delete mode 100644 tests/integration/components/home-page/organization-dashboard/header-test.js delete mode 100644 tests/integration/components/home-page/organization-dashboard/onboarding-guide-test.js delete mode 100644 tests/integration/components/home-page/organization-dashboard/side-nav-test.js delete mode 100644 tests/integration/components/home-page/security-dashboard-nav-test.js create mode 100644 tests/integration/components/security-wrapper-test.js create mode 100644 tests/integration/components/storeknox-wrapper-test.js create mode 100644 tests/integration/components/top-nav-test.js diff --git a/app/components/ak-popover/index.hbs b/app/components/ak-popover/index.hbs index 4d8d89516..c087227fd 100644 --- a/app/components/ak-popover/index.hbs +++ b/app/components/ak-popover/index.hbs @@ -29,6 +29,7 @@ {{#if @arrow}} >[]; diff --git a/app/components/ak-svg/appknox-bg-img.hbs b/app/components/ak-svg/appknox-bg-img.hbs new file mode 100644 index 000000000..5e29be3ed --- /dev/null +++ b/app/components/ak-svg/appknox-bg-img.hbs @@ -0,0 +1,37 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/components/ak-svg/sm-indicator.hbs b/app/components/ak-svg/sm-indicator.hbs new file mode 100644 index 000000000..c2d4084d1 --- /dev/null +++ b/app/components/ak-svg/sm-indicator.hbs @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/app/components/ak-svg/storeknox-bg-img.hbs b/app/components/ak-svg/storeknox-bg-img.hbs new file mode 100644 index 000000000..b20c139af --- /dev/null +++ b/app/components/ak-svg/storeknox-bg-img.hbs @@ -0,0 +1,37 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/components/ak-svg/vp-indicator.hbs b/app/components/ak-svg/vp-indicator.hbs new file mode 100644 index 000000000..27551bdd7 --- /dev/null +++ b/app/components/ak-svg/vp-indicator.hbs @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/app/components/ak-tabs/item/index.scss b/app/components/ak-tabs/item/index.scss index cd016e091..7665ee1cc 100644 --- a/app/components/ak-tabs/item/index.scss +++ b/app/components/ak-tabs/item/index.scss @@ -40,12 +40,13 @@ .active-line { position: relative; - color: var(--ak-tabs-text-color-primary) !important; + color: var(--ak-tabs-text-color-primary); font-weight: var(--ak-tabs-font-weight-bold); &:hover, &:focus { background-color: unset; + color: var(--ak-tabs-text-color-primary); } .ak-tab-badge { diff --git a/app/components/appknox-wrapper/index.hbs b/app/components/appknox-wrapper/index.hbs new file mode 100644 index 000000000..bd99b30ce --- /dev/null +++ b/app/components/appknox-wrapper/index.hbs @@ -0,0 +1,86 @@ +
+ + + +
+ + + {{t 'startNewScan'}} + + + + + + + {{#unless this.orgIsAnEnterprise}} + + <:leftIcon> + + + + <:default> + {{t 'onboardingGuides'}} + + + {{/unless}} + + {{#if this.showKnowledgeBase}} + + <:leftIcon> + + + + <:default>{{t 'knowledgeBase'}} + + {{/if}} + + + + + + + {{! #id is required for scroll-to-top util }} +
+ {{yield}} +
+ + {{#if this.showOnboardingGuide}} + + {{/if}} + + {{#if this.showAddEditModal}} + + {{/if}} +
\ No newline at end of file diff --git a/app/components/home-page/organization-dashboard/index.scss b/app/components/appknox-wrapper/index.scss similarity index 63% rename from app/components/home-page/organization-dashboard/index.scss rename to app/components/appknox-wrapper/index.scss index 7ac87b627..9147414c4 100644 --- a/app/components/home-page/organization-dashboard/index.scss +++ b/app/components/appknox-wrapper/index.scss @@ -1,4 +1,4 @@ -.organization-dashboard-root { +.appknox-wrapper-root { width: 100%; display: grid; grid-template-areas: @@ -34,4 +34,21 @@ .dashboard-sidenav { grid-area: sidenav; } + + .lower-menu-chat { + color: var(--appknox-wrapper-text-primary) !important; + } + + .divider { + border-left: 1px solid var(--appknox-wrapper-border-color); + height: 30px; + } + + .top-nav-content { + flex-grow: 1; + } + + .menu-item-text { + color: var(--appknox-wrapper-text-color) !important; + } } diff --git a/app/components/home-page/organization-dashboard/side-nav/index.ts b/app/components/appknox-wrapper/index.ts similarity index 67% rename from app/components/home-page/organization-dashboard/side-nav/index.ts rename to app/components/appknox-wrapper/index.ts index 306fe4ee9..7ffafe4ac 100644 --- a/app/components/home-page/organization-dashboard/side-nav/index.ts +++ b/app/components/appknox-wrapper/index.ts @@ -1,82 +1,123 @@ -import { inject as service } from '@ember/service'; import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; +import { tracked } from '@glimmer/tracking'; +import type IntlService from 'ember-intl/services/intl'; +import type RouterService from '@ember/routing/router-service'; + import ENV from 'irene/config/environment'; +import type UserModel from 'irene/models/user'; +import type MeService from 'irene/services/me'; +import type FreshdeskService from 'irene/services/freshdesk'; +import type IntegrationService from 'irene/services/integration'; +import type OrganizationService from 'irene/services/organization'; +import type ConfigurationService from 'irene/services/configuration'; +import type { LowerMenuItem, MenuItem } from '../side-nav'; import styles from './index.scss'; -import MeService from 'irene/services/me'; -import OrganizationService from 'irene/services/organization'; -import IntegrationService from 'irene/services/integration'; -import IntlService from 'ember-intl/services/intl'; -import ConfigurationService from 'irene/services/configuration'; -import WhitelabelService from 'irene/services/whitelabel'; -import { action } from '@ember/object'; -import FreshdeskService from 'irene/services/freshdesk'; - -export interface HomePageOrganizationDashboardSideNavSignature { +export interface AppknoxWrapperSignature { Args: { - isSecurityEnabled?: boolean; - isCollapsed: boolean; - toggleSidebar: () => void; + user: UserModel; }; - Element: HTMLElement; -} - -interface MenuItem { - label: string; - icon?: string; - route?: string; - query?: Record; - currentWhen?: string; - hasBadge?: boolean; - badgeLabel?: string; - component?: 'home-page/organization-dashboard/side-nav/security-menu-item'; - customIconComponent?: 'ak-svg/public-api-icon'; -} -interface LowerMenuItem { - title: string; - icon: string; - divider?: boolean; - onClick: () => void; - enablePendo?: boolean; - iconClass?: string; - textClass?: string; - listItemClass?: string; + Blocks: { + default: []; + }; } -export default class HomePageOrganizationDashboardSideNavComponent extends Component { +export default class AppknoxWrapperComponent extends Component { + @service declare ajax: any; + @service declare session: any; @service declare me: MeService; - @service declare organization: OrganizationService; - @service declare integration: IntegrationService; @service declare intl: IntlService; + @service declare router: RouterService; + @service declare freshdesk: FreshdeskService; + @service declare integration: IntegrationService; + @service declare organization: OrganizationService; + @service declare notifications: NotificationService; @service declare configuration: ConfigurationService; - @service declare whitelabel: WhitelabelService; @service('browser/window') declare window: Window; - @service declare freshdesk: FreshdeskService; + + @tracked isSecurityEnabled = false; + @tracked isEmptyOrgName = this.checkIfOrgNameIsEmpty; + @tracked showAddEditModal = this.isEmptyOrgName; + @tracked showOnboardingGuide = false; + @tracked isSidebarCollapsed: boolean; showMarketplace = ENV.enableMarketplace; productVersion = ENV.productVersion; - faviconImage: HTMLImageElement = new Image(); - appLogoImage: HTMLImageElement = new Image(); - - constructor( - owner: unknown, - args: HomePageOrganizationDashboardSideNavSignature['Args'] - ) { + constructor(owner: unknown, args: AppknoxWrapperSignature['Args']) { super(owner, args); - this.faviconImage.src = this.whitelabel.favicon; - this.appLogoImage.src = this.whitelabel.logo; + const storedState = this.window.localStorage.getItem('sidebarState'); + this.isSidebarCollapsed = + storedState !== null ? storedState === 'collapsed' : true; + + this.securityEnabled(); + } + + get checkIfOrgNameIsEmpty() { + const organization = this.organization; + const isOwner = this.me?.org?.get('is_owner'); + + if (isOwner) { + const orgName = organization?.selected?.name; + + if (!orgName) { + return true; + } + } + + return false; + } + + /** + * @property {Boolean} isShowAnalytics + * Property to disable analytics page for member role + */ + get isShowAnalytics() { + return this.me.org?.is_member === false; + } + + get showBilling() { + const orgShowBilling = this.organization?.selected?.showBilling; + const isOwner = this.me.org?.is_owner; + + return orgShowBilling && isOwner; + } + + get showPartnerDashboard() { + return this.me.org?.can_access_partner_dashboard; + } + + get showAppMonitoringDashboard() { + return this.organization?.selected?.features?.app_monitoring; + } + + get showPublicApiDocs() { + return this.organization?.selected?.features?.public_apis; + } + + get showSbomDashboard() { + return ( + this.organization.selected?.features?.sbom && + !this.configuration.serverData.enterprise + ); + } + + get enablePendo() { + return this.integration.isPendoEnabled(); + } + + get enableChatSupport() { + return this.freshdesk.freshchatEnabled; } - get classes() { - return { - menuItemText: styles['menu-item-text'], - menuItemLink: styles['menu-item-link'], - menuItemLinkActive: styles['active'], - menuItemTooltip: styles['menu-item-tooltip'], - }; + get versionText() { + const version = this.productVersion; + const translated = this.intl.t('version'); + return `${translated} - ${version}`; } get menuItems() { @@ -147,10 +188,9 @@ export default class HomePageOrganizationDashboardSideNavComponent extends Compo hasBadge: true, badgeLabel: this.intl.t('beta'), }, - this.args.isSecurityEnabled && { + this.isSecurityEnabled && { label: this.intl.t('security'), - component: - 'home-page/organization-dashboard/side-nav/security-menu-item' as const, + component: 'side-nav/security-menu-item' as const, }, ].filter(Boolean) as MenuItem[]; } @@ -170,72 +210,22 @@ export default class HomePageOrganizationDashboardSideNavComponent extends Compo iconClass: 'pendo-ak-icon', enablePendo: this.enablePendo, onClick: this.showGuide, - divider: true, textClass: styles['menu-item-text'], listItemClass: this.enablePendo ? '' : 'no-hover', }, - { - title: this.args.isCollapsed - ? this.intl.t('expand') - : this.intl.t('collapse'), - icon: 'keyboard-tab', - onClick: this.args.toggleSidebar, - textClass: styles['menu-item-text'], - iconClass: this.isSidebarExpanded ? 'rotated-icon' : '', - }, ].filter(Boolean) as LowerMenuItem[]; } - /** - * @property {Boolean} isShowAnalytics - * Property to disable analytics page for member role - */ - get isShowAnalytics() { - return this.me.org?.is_member === false; + get showKnowledgeBase() { + return this.freshdesk.supportWidgetIsEnabled; } - get showBilling() { - const orgShowBilling = this.organization?.selected?.showBilling; - const isOwner = this.me.org?.is_owner; - - return orgShowBilling && isOwner; - } - - get showPartnerDashboard() { - return this.me.org?.can_access_partner_dashboard; - } - - get showAppMonitoringDashboard() { - return this.organization?.selected?.features?.app_monitoring; - } - - get showPublicApiDocs() { - return this.organization?.selected?.features?.public_apis; - } - - get showSbomDashboard() { - return ( - this.organization.selected?.features?.sbom && - !this.configuration.serverData.enterprise - ); - } - - get enablePendo() { - return this.integration.isPendoEnabled(); - } - - get enableChatSupport() { + get showChatSupport() { return this.freshdesk.freshchatEnabled; } - get isSidebarExpanded() { - return !this.args.isCollapsed; - } - - get versionText() { - const version = this.productVersion; - const translated = this.intl.t('version'); - return `${translated} - ${version}`; + get orgIsAnEnterprise() { + return this.configuration.serverData.enterprise; } @action async showGuide() { @@ -257,10 +247,50 @@ export default class HomePageOrganizationDashboardSideNavComponent extends Compo @action openChatBox() { this.freshdesk.openFreshchatWidget(); } + + @action + toggleSidebar() { + this.isSidebarCollapsed = !this.isSidebarCollapsed; + + this.window.localStorage.setItem( + 'sidebarState', + this.isSidebarCollapsed ? 'collapsed' : 'expanded' + ); + } + + @action + onToggleOnboardingGuide() { + this.showOnboardingGuide = !this.showOnboardingGuide; + } + + @action onOpenKnowledgeBase() { + this.freshdesk.openSupportWidget(); + } + + @action + securityEnabled() { + this.ajax + .request('projects', { + namespace: 'api/hudson-api', + }) + .then( + () => { + this.isSecurityEnabled = true; + }, + () => { + this.isSecurityEnabled = false; + } + ); + } + + @action + handleAddEditOrgNameCancel() { + this.showAddEditModal = false; + } } declare module '@glint/environment-ember-loose/registry' { export default interface Registry { - 'HomePage::OrganizationDashboard::SideNav': typeof HomePageOrganizationDashboardSideNavComponent; + AppknoxWrapper: typeof AppknoxWrapperComponent; } } diff --git a/app/components/home-page/organization-dashboard/onboarding-guide/index.hbs b/app/components/appknox-wrapper/onboarding-guide/index.hbs similarity index 100% rename from app/components/home-page/organization-dashboard/onboarding-guide/index.hbs rename to app/components/appknox-wrapper/onboarding-guide/index.hbs diff --git a/app/components/home-page/organization-dashboard/onboarding-guide/index.scss b/app/components/appknox-wrapper/onboarding-guide/index.scss similarity index 81% rename from app/components/home-page/organization-dashboard/onboarding-guide/index.scss rename to app/components/appknox-wrapper/onboarding-guide/index.scss index 313b7c742..54b0a0f16 100644 --- a/app/components/home-page/organization-dashboard/onboarding-guide/index.scss +++ b/app/components/appknox-wrapper/onboarding-guide/index.scss @@ -4,7 +4,7 @@ } .onboarding-guide-list-container{ - border-right: 1px solid var(--onboarding-guide-list-border-color); + border-right: 1px solid var(--appknox-wrapper-onboarding-guide-list-border-color); height: 407.45px; } diff --git a/app/components/home-page/organization-dashboard/onboarding-guide/index.ts b/app/components/appknox-wrapper/onboarding-guide/index.ts similarity index 88% rename from app/components/home-page/organization-dashboard/onboarding-guide/index.ts rename to app/components/appknox-wrapper/onboarding-guide/index.ts index 52eb05e00..36d3dde9b 100644 --- a/app/components/home-page/organization-dashboard/onboarding-guide/index.ts +++ b/app/components/appknox-wrapper/onboarding-guide/index.ts @@ -3,9 +3,9 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { next } from '@ember/runloop'; import { inject as service } from '@ember/service'; -import IntlService from 'ember-intl/services/intl'; +import type IntlService from 'ember-intl/services/intl'; -interface OrganizationDashboardOnboardingGuideSignature { +interface AppknoxWrapperOnboardingGuideSignature { Args: { onClose: () => void; }; @@ -17,7 +17,7 @@ interface GuideItem { url: string; } -export default class OrganizationDashboardOnboardingGuide extends Component { +export default class AppknoxWrapperOnboardingGuide extends Component { @service declare intl: IntlService; @tracked activeGuide; @@ -25,7 +25,7 @@ export default class OrganizationDashboardOnboardingGuide extends Component scrollOrganizationDashboardMainContainerTo({ top: 0, left: 0 }), - 150 - ); + later(() => scrollDashboardMainContainerTo({ top: 0, left: 0 }), 150); } } diff --git a/app/components/file-compare/vulnerability-details/index.ts b/app/components/file-compare/vulnerability-details/index.ts index b89469afc..ab8865738 100644 --- a/app/components/file-compare/vulnerability-details/index.ts +++ b/app/components/file-compare/vulnerability-details/index.ts @@ -9,7 +9,7 @@ import { tracked } from '@glimmer/tracking'; import FileModel from 'irene/models/file'; import VulnerabilityModel from 'irene/models/vulnerability'; import { getComputedRiskCategory } from 'irene/utils/compare-files'; -import { scrollOrganizationDashboardMainContainerTo } from 'irene/utils/scroll-to-top'; +import { scrollDashboardMainContainerTo } from 'irene/utils/scroll-to-top'; interface FileCompareVulnerabilityDetailsSignature { Args: { @@ -132,7 +132,7 @@ export default class FileCompareVulnerabilityDetailsComponent extends Component< handleExpandFilesOverview() { this.expandFilesOverview = !this.expandFilesOverview; - scrollOrganizationDashboardMainContainerTo({ top: 0, behavior: 'smooth' }); + scrollDashboardMainContainerTo({ top: 0, behavior: 'smooth' }); } get fileAnalyses() { diff --git a/app/components/home-page/index.hbs b/app/components/home-page/index.hbs index 3d3040e43..3390ebe0c 100644 --- a/app/components/home-page/index.hbs +++ b/app/components/home-page/index.hbs @@ -1,25 +1,69 @@ -{{#if this.isLoaded}} - {{#if this.isSecurityDashboard}} - - - {{yield}} - {{else}} - + + - {{yield}} - - {{/if}} -{{/if}} + +
+ + + + {{t 'selectThePath'}} + + + + + + {{t 'toSecureYourMobileApps'}} + + + + <:leftIcon> + + + + <:default> + {{t 'logout'}} + + + + + + -{{#if this.showAddEditModal}} - -{{/if}} \ No newline at end of file + + {{#each this.productCardDetails as |productCardDetails|}} + + {{/each}} + +
+ \ No newline at end of file diff --git a/app/components/home-page/index.js b/app/components/home-page/index.js deleted file mode 100644 index 8598aa66f..000000000 --- a/app/components/home-page/index.js +++ /dev/null @@ -1,89 +0,0 @@ -import { action } from '@ember/object'; -import { inject as service } from '@ember/service'; -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import triggerAnalytics from 'irene/utils/trigger-analytics'; - -export default class HomePageComponent extends Component { - @service me; - @service intl; - @service ajax; - @service session; - @service organization; - @service integration; - @service notifications; - @service freshdesk; - @service('browser/window') window; - - @tracked isLoaded = false; - @tracked isSecurityEnabled = false; - @tracked isSecurityDashboard = false; - @tracked isEmptyOrgName = this.checkIfOrgNameIsEmpty; - @tracked showAddEditModal = this.isEmptyOrgName; - - tSomethingWentWrong = this.intl.t('somethingWentWrong'); - tOrganizationNameUpdated = this.intl.t('organizationNameUpdated'); - - constructor(...args) { - super(...args); - this.securityEnabled(); - } - - get checkIfOrgNameIsEmpty() { - const organization = this.organization; - const isOwner = this.me?.org?.get('is_owner'); - - if (isOwner) { - const orgName = organization.selected.name; - if (!orgName) { - return true; - } - } - - return false; - } - - @action securityEnabled() { - this.ajax - .request('projects', { - namespace: 'api/hudson-api', - }) - .then( - () => { - this.isSecurityEnabled = true; - this.securityDashboard(); - }, - () => { - this.isSecurityEnabled = false; - this.securityDashboard(); - } - ); - } - - @action securityDashboard() { - if (this.window.location.pathname.startsWith('/security')) { - const isSecurityEnabled = this.isSecurityEnabled; - if (isSecurityEnabled) { - this.isSecurityDashboard = true; - } else { - this.getOwner(this) - .lookup('route:authenticated') - .transitionTo('authenticated.dashboard.projects'); - } - this.isLoaded = true; - } else { - this.isLoaded = true; - } - } - - @action invalidateSession() { - this.freshdesk.logUserOutOfSupportWidget(); - triggerAnalytics('logout'); - this.session.invalidate(); - } - - @action - handleCancel() { - this.showAddEditModal = false; - } -} diff --git a/app/components/home-page/index.scss b/app/components/home-page/index.scss new file mode 100644 index 000000000..e5672ec68 --- /dev/null +++ b/app/components/home-page/index.scss @@ -0,0 +1,23 @@ +.home-page-container { + background-color: var(--home-page-background-color); + min-height: 100vh; + height: auto; + padding: 4em; + + .home-page-content { + max-width: 1200px; + } +} + +.logo-container { + width: 100%; + + img { + max-width: 225px; + max-height: 100px; + } +} + +.logout-button { + background-color: var(--home-page-button-background-color); +} \ No newline at end of file diff --git a/app/components/home-page/index.ts b/app/components/home-page/index.ts new file mode 100644 index 000000000..f79595005 --- /dev/null +++ b/app/components/home-page/index.ts @@ -0,0 +1,62 @@ +import { service } from '@ember/service'; +import Component from '@glimmer/component'; +import type IntlService from 'ember-intl/services/intl'; + +import Router from 'irene/router'; +import type OrganizationService from 'irene/services/organization'; +import type ConfigurationService from 'irene/services/configuration'; +import type UserAuthService from 'irene/services/user-auth'; + +interface productCardDetails { + title: string; + desc: string; + link: string; + indicator: 'ak-svg/sm-indicator' | 'ak-svg/vp-indicator'; + coverImage: 'ak-svg/storeknox-bg-img' | 'ak-svg/appknox-bg-img'; +} + +export default class HomePageComponent extends Component { + @service declare router: Router; + @service declare intl: IntlService; + @service declare organization: OrganizationService; + @service declare configuration: ConfigurationService; + @service declare userAuth: UserAuthService; + @service declare session: any; + + get isStoreknoxEnabled() { + return this.organization.selected?.features.app_monitoring; + } + + get orgIsAnEnterprise() { + return this.configuration.serverData.enterprise; + } + + get productCardDetails() { + return [ + { + title: this.orgIsAnEnterprise + ? this.intl.t('vapt') + : this.intl.t('appknox'), + desc: this.intl.t('appknoxDesc'), + link: 'authenticated.dashboard.projects', + indicator: 'ak-svg/vp-indicator', + coverImage: 'ak-svg/appknox-bg-img', + }, + this.isStoreknoxEnabled && { + title: this.orgIsAnEnterprise + ? this.intl.t('appMonitoring') + : this.intl.t('storeknox'), + desc: this.intl.t('storeknoxDesc'), + link: 'authenticated.storeknox.discover', + indicator: 'ak-svg/sm-indicator', + coverImage: 'ak-svg/storeknox-bg-img', + }, + ].filter(Boolean) as productCardDetails[]; + } +} + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry { + HomePage: typeof HomePageComponent; + } +} diff --git a/app/components/home-page/organization-dashboard/header/index.hbs b/app/components/home-page/organization-dashboard/header/index.hbs deleted file mode 100644 index b2085e08a..000000000 --- a/app/components/home-page/organization-dashboard/header/index.hbs +++ /dev/null @@ -1,93 +0,0 @@ - - - - {{t 'startNewScan'}} - - - - - - - {{#unless this.orgIsAnEnterprise}} - - <:leftIcon> - - - - <:default> - {{t 'onboardingGuides'}} - - - {{/unless}} - - {{#if this.showKnowledgeBase}} - - <:leftIcon> - - - - <:default>{{t 'knowledgeBase'}} - - {{/if}} - - - - - <:leftIcon> - - - - <:default>{{@user.username}} - - <:rightIcon> - - - - - - - - {{#each this.profileMenuItems as |pmi|}} - - - - {{pmi.label}} - - {{/each}} - \ No newline at end of file diff --git a/app/components/home-page/organization-dashboard/header/index.scss b/app/components/home-page/organization-dashboard/header/index.scss deleted file mode 100644 index 9a813b7a4..000000000 --- a/app/components/home-page/organization-dashboard/header/index.scss +++ /dev/null @@ -1,20 +0,0 @@ -.navbar-root { - padding: 0.65em 1em; - background-color: var(--organization-dashboard-header-background-color); - color: var(--organization-dashboard-header-text-color); - max-height: 4em; - - .navbar-btn { - color: var(--organization-dashboard-header-text-color); - background-color: unset; - - :global(.ak-icon) { - font-size: 1.5rem !important; - } - - :hover { - color: var(--organization-dashboard-header-text-color); - background-color: unset; - } - } -} diff --git a/app/components/home-page/organization-dashboard/index.hbs b/app/components/home-page/organization-dashboard/index.hbs deleted file mode 100644 index 3520267a7..000000000 --- a/app/components/home-page/organization-dashboard/index.hbs +++ /dev/null @@ -1,32 +0,0 @@ -
- - - - - {{! #id is required for scroll-to-top util }} -
- {{yield}} -
- - {{#if this.showOnboardingGuide}} - - {{/if}} -
\ No newline at end of file diff --git a/app/components/home-page/organization-dashboard/index.ts b/app/components/home-page/organization-dashboard/index.ts deleted file mode 100644 index 0bf41c87d..000000000 --- a/app/components/home-page/organization-dashboard/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { action } from '@ember/object'; -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import { inject as service } from '@ember/service'; - -import UserModel from 'irene/models/user'; - -export interface HomePageOrganizationDashboardSignature { - Args: { - logoutAction: () => void; - isSecurityEnabled?: boolean; - user: UserModel; - }; - - Blocks: { - default: []; - }; -} - -export default class HomePageOrganizationDashboardComponent extends Component { - @service('browser/window') declare window: Window; - - @tracked isSidebarCollapsed: boolean; - @tracked showOnboardingGuide = false; - - constructor( - owner: unknown, - args: HomePageOrganizationDashboardSignature['Args'] - ) { - super(owner, args); - - const storedState = this.window.localStorage.getItem('sidebarState'); - - this.isSidebarCollapsed = - storedState !== null ? storedState === 'collapsed' : true; - } - - @action - toggleSidebar() { - this.isSidebarCollapsed = !this.isSidebarCollapsed; - - this.window.localStorage.setItem( - 'sidebarState', - this.isSidebarCollapsed ? 'collapsed' : 'expanded' - ); - } - - @action - onToggleOnboardingGuide() { - this.showOnboardingGuide = !this.showOnboardingGuide; - } -} - -declare module '@glint/environment-ember-loose/registry' { - export default interface Registry { - 'HomePage::OrganizationDashboard': typeof HomePageOrganizationDashboardComponent; - } -} diff --git a/app/components/home-page/organization-dashboard/side-nav/security-menu-item/index.ts b/app/components/home-page/organization-dashboard/side-nav/security-menu-item/index.ts deleted file mode 100644 index 3b50f7288..000000000 --- a/app/components/home-page/organization-dashboard/side-nav/security-menu-item/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import Component from '@glimmer/component'; - -export interface OrganizationDashboardSideNavSecurityMenuItemSignature { - Args: { - isCollapsed: boolean; - classes: { - menuItemText?: string; - menuItemLink?: string; - menuItemLinkActive?: string; - menuItemTooltip?: string; - }; - }; - Element: HTMLElement; -} - -export default class OrganizationDashboardSideNavSecurityMenuItemComponent extends Component {} - -declare module '@glint/environment-ember-loose/registry' { - export default interface Registry { - 'HomePage::OrganizationDashboard::SideNav::SecurityMenuItem': typeof OrganizationDashboardSideNavSecurityMenuItemComponent; - 'home-page/organization-dashboard/side-nav/security-menu-item': typeof OrganizationDashboardSideNavSecurityMenuItemComponent; - } -} diff --git a/app/components/home-page/product-card/index.hbs b/app/components/home-page/product-card/index.hbs new file mode 100644 index 000000000..0f87138b6 --- /dev/null +++ b/app/components/home-page/product-card/index.hbs @@ -0,0 +1,45 @@ + + {{#let (component @coverBackgroundImage) as |Cover|}} + + {{/let}} + + + + {{#let (component @indicatorSvg) as |SvgIcon|}} + + {{/let}} + + + {{@name}} + + + + {{@description}} + + + + + + {{@linkText}} + + + + + + \ No newline at end of file diff --git a/app/components/home-page/product-card/index.scss b/app/components/home-page/product-card/index.scss new file mode 100644 index 000000000..94aa16262 --- /dev/null +++ b/app/components/home-page/product-card/index.scss @@ -0,0 +1,15 @@ +.product-card-container { + border-radius: var(--home-page-product-card-border-radius); + border: 1px solid var(--home-page-product-card-border-color); + max-width: 314px; + + .product-card-content { + background-color: var(--home-page-product-card-background-color); + padding: 1.5em; + } + + .product-icon { + width: 42px; + height: 42px; + } +} \ No newline at end of file diff --git a/app/components/home-page/product-card/index.ts b/app/components/home-page/product-card/index.ts new file mode 100644 index 000000000..ca45b1980 --- /dev/null +++ b/app/components/home-page/product-card/index.ts @@ -0,0 +1,32 @@ +import Component from '@glimmer/component'; +import { service } from '@ember/service'; +import type IntlService from 'ember-intl/services/intl'; + +export interface ProductCardComponentSignature { + Element: HTMLElement; + Args: { + name: string; + description: string; + linkText: string; + route: string; + coverBackgroundImage: 'ak-svg/storeknox-bg-img' | 'ak-svg/appknox-bg-img'; + indicatorSvg: 'ak-svg/sm-indicator' | 'ak-svg/vp-indicator'; + }; + Blocks: { + default: []; + }; +} + +export default class ProductCardComponent extends Component { + @service declare intl: IntlService; + + get route() { + return this.args.route; + } +} + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry { + 'HomePage::ProductCard': typeof ProductCardComponent; + } +} diff --git a/app/components/home-page/security-dashboard-nav/index.hbs b/app/components/home-page/security-dashboard-nav/index.hbs deleted file mode 100644 index 904a4deb8..000000000 --- a/app/components/home-page/security-dashboard-nav/index.hbs +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - <:leftIcon> - - - - <:default>{{@user.username}} - - <:rightIcon> - - - - - - - {{#each this.profileMenuItems as |pmi|}} - - - {{pmi.label}} - - {{/each}} - \ No newline at end of file diff --git a/app/components/home-page/security-dashboard-nav/index.scss b/app/components/home-page/security-dashboard-nav/index.scss deleted file mode 100644 index cd68e1827..000000000 --- a/app/components/home-page/security-dashboard-nav/index.scss +++ /dev/null @@ -1,17 +0,0 @@ -.appknox-logo { - pointer-events: none; - padding: 1em 0; - - img { - max-width: 12em; - max-height: 2em; - } -} - -.security-top-nav { - max-height: 4em; - padding: 0 1.5em; - margin-bottom: 1.5em; - background-color: var(--security-dashboard-nav-bg-color); - align-items: unset; -} diff --git a/app/components/home-page/security-dashboard-nav/index.ts b/app/components/home-page/security-dashboard-nav/index.ts deleted file mode 100644 index d787af710..000000000 --- a/app/components/home-page/security-dashboard-nav/index.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { action } from '@ember/object'; -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; - -import UserModel from 'irene/models/user'; - -export interface HomePageSecurityDashboardNavSignature { - Args: { - logoutAction: () => void; - user: UserModel; - }; -} - -export default class HomePageSecurityDashboardNavComponent extends Component { - @tracked profileAnchorRef: HTMLElement | null = null; - - get profileMenuItems() { - return [ - { - label: this.args.user.username, - iconName: 'account-circle', - }, - { - label: this.args.user.email, - iconName: 'mail', - }, - { - label: 'Logout', - iconName: 'logout', - color: 'primary', - onClick: this.args.logoutAction, - isLast: true, - }, - ]; - } - - get menuItems() { - return [ - { - id: 'projects', - route: 'authenticated.security.projects', - label: 'Projects', - currentWhen: - 'authenticated.security.projects authenticated.security.files authenticated.security.file', - }, - { - id: 'downloadapp', - route: 'authenticated.security.downloadapp', - label: 'Download App', - currentWhen: 'authenticated.security.downloadapp', - }, - { - id: 'purgeanalysis', - route: 'authenticated.security.purgeanalysis', - label: 'Purge API Analyses', - currentWhen: 'authenticated.security.purgeanalysis', - }, - ]; - } - - @action - toggleProfileMenuView(event: MouseEvent) { - if (this.profileAnchorRef) { - this.handleProfileMenuClose(); - - return; - } - - this.profileAnchorRef = event.currentTarget as HTMLElement; - } - - @action - handleProfileMenuClose() { - this.profileAnchorRef = null; - } -} - -declare module '@glint/environment-ember-loose/registry' { - export default interface Registry { - 'HomePage::SecurityDashboardNav': typeof HomePageSecurityDashboardNavComponent; - } -} diff --git a/app/components/partner/template.hbs b/app/components/partner/template.hbs index 998767009..aace77ae6 100644 --- a/app/components/partner/template.hbs +++ b/app/components/partner/template.hbs @@ -1,23 +1,27 @@ -
-
-
-
Partner Dashboard
-
{{this.organization.selected.name}}
-
-
- -
-
-
+ +
+
+
+
Partner Dashboard
+
{{this.organization.selected.name}}
+
+
+ +
+
+ + {{yield}} +
+
\ No newline at end of file diff --git a/app/components/security-wrapper/index.hbs b/app/components/security-wrapper/index.hbs new file mode 100644 index 000000000..967bccfde --- /dev/null +++ b/app/components/security-wrapper/index.hbs @@ -0,0 +1,10 @@ + + + + +{{yield}} \ No newline at end of file diff --git a/app/components/security-wrapper/index.scss b/app/components/security-wrapper/index.scss new file mode 100644 index 000000000..749dd173f --- /dev/null +++ b/app/components/security-wrapper/index.scss @@ -0,0 +1,3 @@ +.security-header { + margin-bottom: 1.5em; +} \ No newline at end of file diff --git a/app/components/security-wrapper/index.ts b/app/components/security-wrapper/index.ts new file mode 100644 index 000000000..a4b821fb4 --- /dev/null +++ b/app/components/security-wrapper/index.ts @@ -0,0 +1,45 @@ +import Component from '@glimmer/component'; + +import type UserModel from 'irene/models/user'; + +export interface SecurityWrapperSignature { + Args: { + user: UserModel; + }; + + Blocks: { + default: []; + }; +} + +export default class SecurityWrapperComponent extends Component { + get menuItems() { + return [ + { + id: 'projects', + route: 'authenticated.security.projects', + label: 'Projects', + currentWhen: + 'authenticated.security.projects authenticated.security.files authenticated.security.file', + }, + { + id: 'downloadapp', + route: 'authenticated.security.downloadapp', + label: 'Download App', + currentWhen: 'authenticated.security.downloadapp', + }, + { + id: 'purgeanalysis', + route: 'authenticated.security.purgeanalysis', + label: 'Purge API Analyses', + currentWhen: 'authenticated.security.purgeanalysis', + }, + ]; + } +} + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry { + SecurityWrapper: typeof SecurityWrapperComponent; + } +} diff --git a/app/components/security/nav-menu/index.hbs b/app/components/security/nav-menu/index.hbs index 1027678b2..9790dd707 100644 --- a/app/components/security/nav-menu/index.hbs +++ b/app/components/security/nav-menu/index.hbs @@ -1,7 +1,6 @@ @@ -9,6 +8,7 @@ {{#each this.menuItems as |item|}} + {{yield (hash classes=this.classes)}} +
+ {{#if this.showAppMonitoringDashboard}} + + {{/if}} +
- - {{#each this.menuItems as |it|}} + + {{#each @menuItems as |it|}} {{#if it.component}} {{else if it.customIconComponent}} {{#let (component it.customIconComponent) as |CustomIcon|}} - + {{/let}} {{/if}} @@ -92,6 +110,15 @@ {{#each this.lowerMenuItems as |lmi|}} + {{#if lmi.divider}} + + {{/if}} + - - {{#if lmi.divider}} - - {{/if}} {{/each}} \ No newline at end of file diff --git a/app/components/home-page/organization-dashboard/side-nav/index.scss b/app/components/side-nav/index.scss similarity index 65% rename from app/components/home-page/organization-dashboard/side-nav/index.scss rename to app/components/side-nav/index.scss index 9e368bfae..af23ade66 100644 --- a/app/components/home-page/organization-dashboard/side-nav/index.scss +++ b/app/components/side-nav/index.scss @@ -4,10 +4,10 @@ padding-top: 0.5em; padding-bottom: 0.5em; box-sizing: border-box; - z-index: var(--organization-dashboard-side-nav-zIndex); - background-color: var(--organization-dashboard-side-nav-bg-color); - box-shadow: var(--organization-dashboard-side-nav-box-shadow); - border-right: var(--organization-dashboard-side-nav-border); + z-index: var(--side-nav-zIndex); + background-color: var(--side-nav-inherit-background); + box-shadow: var(--side-nav-box-shadow); + border-right: var(--side-nav-border); display: flex; flex-direction: column; @@ -22,18 +22,13 @@ width: 75%; } - .scroll-container { - display: flex; - flex: 1; - } - .side-menu-list { margin: auto; } } .app-logo { - margin: 3em auto; + margin: 3em auto 1em; max-width: 56%; max-height: 9em; @@ -64,6 +59,10 @@ flex-direction: column; box-sizing: border-box; overflow-x: hidden; + + &.no-switcher { + padding-top: 3em; + } } .scroll-container { @@ -87,14 +86,14 @@ &:hover, &:focus { background-color: var( - --organization-dashboard-side-nav-item-hover-background + --side-nav-inherit-hover-background ) !important; } &.active { position: relative; background-color: var( - --organization-dashboard-side-nav-item-active-background + --side-nav-item-active-background ); &::before { @@ -103,7 +102,7 @@ width: 2px; height: 100%; background-color: var( - --organization-dashboard-side-nav-item-active-border-color + --side-nav-item-active-border-color ); position: absolute; left: 0; @@ -111,21 +110,22 @@ } .menu-item-text { - font-weight: var(--organization-dashboard-side-nav-font-weight-medium); + font-weight: var(--side-nav-font-weight-medium); + color: var(--side-nav-inherit-background-text-color); } .menu-item-badge { height: 18px !important; background-color: var( - --organization-dashboard-side-nav-item-active-badge-background + --side-nav-item-active-badge-background ); span { color: var( - --organization-dashboard-side-nav-item-active-badge-text-color + --side-nav-item-active-badge-text-color ); font-weight: var( - --organization-dashboard-side-nav-font-weight-medium + --side-nav-font-weight-medium ); opacity: unset; } @@ -135,24 +135,25 @@ .menu-item-badge { height: 18px !important; background-color: var( - --organization-dashboard-side-nav-item-badge-background + --side-nav-item-badge-background ); span { - color: var(--organization-dashboard-side-nav-item-badge-text-color); - font-weight: var(--organization-dashboard-side-nav-font-weight-medium); + color: var(--side-nav-item-badge-text-color); + font-weight: var(--side-nav-font-weight-medium); opacity: 0.4; } } } - .menu-item-icon, - .menu-item-text { - color: var(--organization-dashboard-side-nav-text-color) !important; + .menu-item-icon, .menu-item-text { + color: var(--side-nav-inherit-background-text-color) !important; } - .submission-container { - height: 100%; + .menu-item-icon-svg { + path { + stroke: var(--side-nav-inherit-background-text-color) !important; + } } .lower-menu { @@ -174,7 +175,7 @@ } .lower-menu-chat { - color: var(--organization-dashboard-side-nav-text-primary) !important; + color: var(--side-nav-text-primary) !important; } } diff --git a/app/components/side-nav/index.ts b/app/components/side-nav/index.ts new file mode 100644 index 000000000..501a33d73 --- /dev/null +++ b/app/components/side-nav/index.ts @@ -0,0 +1,130 @@ +import Component from '@glimmer/component'; +import { inject as service } from '@ember/service'; +import type IntlService from 'ember-intl/services/intl'; + +import type MeService from 'irene/services/me'; +import type OrganizationService from 'irene/services/organization'; +import type IntegrationService from 'irene/services/integration'; +import type WhitelabelService from 'irene/services/whitelabel'; +import styles from './index.scss'; + +type DefaultBlock = { + classes: { + menuItemLink?: string; + menuItemLinkActive?: string; + menuItemTooltip?: string; + menuItemIcon?: string; + menuItemText?: string; + }; +}; + +export interface SideNavSignature { + Args: { + menuItems: MenuItem[]; + lowerMenuItems?: LowerMenuItem[]; + isSecurityEnabled?: boolean; + isCollapsed: boolean; + toggleSidebar: () => void; + productSwitcherFilterKey: string; + }; + Element: HTMLElement; + Blocks: { + default: [DefaultBlock]; + }; +} + +export interface MenuItem { + label: string; + icon?: string; + route?: string; + query?: Record; + currentWhen?: string; + hasBadge?: boolean; + badgeLabel?: string; + component?: 'side-nav/security-menu-item'; + customIconComponent?: 'ak-svg/public-api-icon'; +} + +export interface LowerMenuItem { + title: string; + icon: string; + divider?: boolean; + onClick: () => void; + enablePendo?: boolean; + iconClass?: string; + textClass?: string; + listItemClass?: string; +} + +export interface SwitcherMenuItem { + id: string; + svg: 'ak-svg/sm-indicator' | 'ak-svg/vp-indicator'; + label: string; + route: string; + key: string; +} + +export default class SideNavComponent extends Component { + @service declare me: MeService; + @service declare organization: OrganizationService; + @service declare integration: IntegrationService; + @service declare intl: IntlService; + @service declare whitelabel: WhitelabelService; + @service('browser/window') declare window: Window; + + faviconImage: HTMLImageElement = new Image(); + appLogoImage: HTMLImageElement = new Image(); + + constructor(owner: unknown, args: SideNavSignature['Args']) { + super(owner, args); + + this.faviconImage.src = this.whitelabel.favicon; + this.appLogoImage.src = this.whitelabel.logo; + } + + get classes() { + return { + menuItemText: styles['menu-item-text'], + menuItemLink: styles['menu-item-link'], + menuItemLinkActive: styles['active'], + menuItemTooltip: styles['menu-item-tooltip'], + menuItemIcon: styles['menu-item-icon'], + }; + } + + get commonLowerMenuItems() { + return [ + { + title: this.args.isCollapsed + ? this.intl.t('expand') + : this.intl.t('collapse'), + icon: 'keyboard-tab', + onClick: this.args.toggleSidebar, + textClass: styles['menu-item-text'], + iconClass: this.isSidebarExpanded ? 'rotated-icon' : '', + divider: true, + }, + ] as LowerMenuItem[]; + } + + get lowerMenuItems() { + return [ + ...(this.args.lowerMenuItems ?? []), + ...this.commonLowerMenuItems, + ] as LowerMenuItem[]; + } + + get isSidebarExpanded() { + return !this.args.isCollapsed; + } + + get showAppMonitoringDashboard() { + return this.organization?.selected?.features?.app_monitoring; + } +} + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry { + SideNav: typeof SideNavComponent; + } +} diff --git a/app/components/side-nav/product-switcher/index.hbs b/app/components/side-nav/product-switcher/index.hbs new file mode 100644 index 000000000..00247f866 --- /dev/null +++ b/app/components/side-nav/product-switcher/index.hbs @@ -0,0 +1,81 @@ + + + +
+ + + {{#if this.isSidebarExpanded}} + + + {{t 'appSwitcher'}} + + + + + {{/if}} +
+
+ + + + +
+ + {{t 'switchTo'}} + + + {{#each this.switcherMenuItems as |switcherItem|}} + + + {{#let (component switcherItem.svg) as |SvgIcon|}} + + {{/let}} + + + {{switcherItem.label}} + + + + {{/each}} +
+
\ No newline at end of file diff --git a/app/components/side-nav/product-switcher/index.scss b/app/components/side-nav/product-switcher/index.scss new file mode 100644 index 000000000..8ca2c3874 --- /dev/null +++ b/app/components/side-nav/product-switcher/index.scss @@ -0,0 +1,57 @@ +.switcher { + width: 100%; + display: flex; + align-items: center; + cursor: pointer; + + &.collapsed { + justify-content: center; + } +} + +.switcher-menu-tooltip { + display: flex; + width: 100%; + padding: 1em; + + &.collapsed { + justify-content: center; + } + + &:hover { + background-color: var(--side-nav-product-switcher-item-hover-background); + } +} + +.switcher-menu-container { + margin-left: 0.8em; // offset for arrow + border: var(--side-nav-product-switcher-border); + box-shadow: var(--side-nav-product-switcher-box-shadow); + min-width: 200px; + background-color: var(--side-nav-product-switcher-bg-main); + + .switcher-menu-item { + width: 100%; + margin: 0.3em 0; + padding: 0.75em 1em; + justify-content: flex-start; + + &:hover { + background-color: var(--side-nav-product-switcher-item-hover-background); + } + } + + .switcher-menu-title { + text-transform: uppercase; + font-size: 0.857rem; + padding: 1.2em; + background-color: var(--side-nav-product-switcher-modal-title-background); + border-bottom: var(--side-nav-product-switcher-border); + } +} + +.switcher-popover-arrow { + &::before { + background-color: var(--side-nav-product-switcher-modal-title-background); + } +} \ No newline at end of file diff --git a/app/components/side-nav/product-switcher/index.ts b/app/components/side-nav/product-switcher/index.ts new file mode 100644 index 000000000..6a1b70934 --- /dev/null +++ b/app/components/side-nav/product-switcher/index.ts @@ -0,0 +1,84 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; +import type IntlService from 'ember-intl/services/intl'; + +import styles from './index.scss'; +import type { SwitcherMenuItem } from '..'; +import type ConfigurationService from 'irene/services/configuration'; + +export interface SideNavProductSwitcherSignature { + Args: { + isCollapsed: boolean; + productSwitcherFilterKey: string; + classes: { + menuItemText?: string; + menuItemIcon?: string; + }; + }; + Element: HTMLElement; +} + +export default class SideNavProductSwitcherComponent extends Component { + @service declare intl: IntlService; + @service declare configuration: ConfigurationService; + + @tracked anchorRef: HTMLElement | null = null; + + get classes() { + return { + switcherPopoverArrow: styles['switcher-popover-arrow'], + }; + } + + get orgIsAnEnterprise() { + return this.configuration.serverData.enterprise; + } + + get isSidebarExpanded() { + return !this.args.isCollapsed; + } + + get switcherMenuItems() { + const allMenuItems: SwitcherMenuItem[] = [ + { + id: 'vp-svg', + svg: 'ak-svg/vp-indicator', + label: this.orgIsAnEnterprise + ? this.intl.t('vapt') + : this.intl.t('appknox'), + route: 'authenticated.dashboard.projects', + key: 'appknox', + }, + { + id: 'sm-svg', + svg: 'ak-svg/sm-indicator', + label: this.orgIsAnEnterprise + ? this.intl.t('appMonitoring') + : this.intl.t('storeknox'), + route: 'authenticated.storeknox.discover', + key: 'storeknox', + }, + ]; + + return allMenuItems.filter( + (item) => item.key !== this.args.productSwitcherFilterKey + ); + } + + @action onClickSwitcher(event: MouseEvent) { + this.anchorRef = event.currentTarget as HTMLElement; + } + + @action closeSwitcherPopover() { + this.anchorRef = null; + } +} + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry { + 'SideNav::ProductSwitcher': typeof SideNavProductSwitcherComponent; + 'side-nav/product-switcher': typeof SideNavProductSwitcherComponent; + } +} diff --git a/app/components/home-page/organization-dashboard/side-nav/security-menu-item/index.hbs b/app/components/side-nav/security-menu-item/index.hbs similarity index 100% rename from app/components/home-page/organization-dashboard/side-nav/security-menu-item/index.hbs rename to app/components/side-nav/security-menu-item/index.hbs diff --git a/app/components/side-nav/security-menu-item/index.ts b/app/components/side-nav/security-menu-item/index.ts new file mode 100644 index 000000000..66eab357d --- /dev/null +++ b/app/components/side-nav/security-menu-item/index.ts @@ -0,0 +1,23 @@ +import Component from '@glimmer/component'; + +export interface SideNavSecurityMenuItemSignature { + Args: { + isCollapsed: boolean; + classes: { + menuItemText?: string; + menuItemLink?: string; + menuItemLinkActive?: string; + menuItemTooltip?: string; + }; + }; + Element: HTMLElement; +} + +export default class SideNavSecurityMenuItemComponent extends Component {} + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry { + 'SideNav::SecurityMenuItem': typeof SideNavSecurityMenuItemComponent; + 'side-nav/security-menu-item': typeof SideNavSecurityMenuItemComponent; + } +} diff --git a/app/components/storeknox-wrapper/index.hbs b/app/components/storeknox-wrapper/index.hbs new file mode 100644 index 000000000..528ad1bf0 --- /dev/null +++ b/app/components/storeknox-wrapper/index.hbs @@ -0,0 +1,26 @@ +
+ + + + + {{! #id is required for scroll-to-top util }} +
+ {{yield}} +
+
\ No newline at end of file diff --git a/app/components/storeknox-wrapper/index.scss b/app/components/storeknox-wrapper/index.scss new file mode 100644 index 000000000..61ae773f3 --- /dev/null +++ b/app/components/storeknox-wrapper/index.scss @@ -0,0 +1,38 @@ +.storeknox-root { + width: 100%; + display: grid; + grid-template-areas: + "sidenav topnav" + "sidenav main"; + height: 100vh; + grid-template-rows: 4em auto; + + &.expanded, &.collapsed { + transition: grid-template-columns 0.2s ease-in-out; + } + + &.collapsed { + grid-template-columns: 56px auto; + } + + &.expanded { + grid-template-columns: 250px auto; + } + + .storeknox-main { + grid-area: main; + max-height: 100vh; + padding: 0.5em; + box-sizing: border-box; + overflow-y: auto; + } + + .storeknox-header { + grid-area: topnav; + } + + .storeknox-sidenav { + grid-area: sidenav; + } + } + \ No newline at end of file diff --git a/app/components/storeknox-wrapper/index.ts b/app/components/storeknox-wrapper/index.ts new file mode 100644 index 000000000..96159200f --- /dev/null +++ b/app/components/storeknox-wrapper/index.ts @@ -0,0 +1,75 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { service } from '@ember/service'; +import { tracked } from '@glimmer/tracking'; +import type IntlService from 'ember-intl/services/intl'; + +import type UserModel from 'irene/models/user'; +import type { MenuItem } from '../side-nav'; +import type ConfigurationService from 'irene/services/configuration'; + +export interface StoreknoxWrapperComponentSignature { + Args: { + user: UserModel; + }; + Blocks: { + default: []; + }; +} + +export default class StoreknoxWrapperComponent extends Component { + @service('browser/window') declare window: Window; + @service declare intl: IntlService; + @service declare configuration: ConfigurationService; + + @tracked isSidebarCollapsed: boolean; + + constructor( + owner: unknown, + args: StoreknoxWrapperComponentSignature['Args'] + ) { + super(owner, args); + + const storedState = this.window.localStorage.getItem('sidebarState'); + + this.isSidebarCollapsed = + storedState !== null ? storedState === 'collapsed' : true; + } + + get storeknoxMenuItems() { + return [ + { + label: this.intl.t('discovery'), + icon: 'search', + route: 'authenticated.storeknox.discover', + currentWhen: 'authenticated.storeknox.discover', + }, + { + label: this.intl.t('inventory'), + icon: 'inventory-2', + route: 'authenticated.storeknox.inventory', + currentWhen: 'authenticated.storeknox.inventory', + }, + ] as MenuItem[]; + } + + get orgIsAnEnterprise() { + return this.configuration.serverData.enterprise; + } + + @action + toggleSidebar() { + this.isSidebarCollapsed = !this.isSidebarCollapsed; + + this.window.localStorage.setItem( + 'sidebarState', + this.isSidebarCollapsed ? 'collapsed' : 'expanded' + ); + } +} + +declare module '@glint/environment-ember-loose/registry' { + export default interface Registry { + StoreknoxWrapper: typeof StoreknoxWrapperComponent; + } +} diff --git a/app/components/top-nav/index.hbs b/app/components/top-nav/index.hbs new file mode 100644 index 000000000..457abad83 --- /dev/null +++ b/app/components/top-nav/index.hbs @@ -0,0 +1,74 @@ + + + {{#if @title}} + + {{@title}} + + {{else}} + + + + {{/if}} + +
+ {{yield (hash classes=this.classes)}} +
+ + + {{#if this.showNotifications}} + + {{/if}} + + + <:leftIcon> + + + + <:default>{{@user.username}} + + <:rightIcon> + + + + +
+
+ + + {{#each this.profileMenuItems as |pmi|}} + + + + + {{pmi.label}} + + + {{/each}} + \ No newline at end of file diff --git a/app/components/top-nav/index.scss b/app/components/top-nav/index.scss new file mode 100644 index 000000000..c0cd9430d --- /dev/null +++ b/app/components/top-nav/index.scss @@ -0,0 +1,43 @@ +.navbar-root { + padding: 0.65em 1em; + background-color: var(--top-nav-background-color); + color: var(--top-nav-text-color); + max-height: 4em; + width: 100%; + + .navbar-btn { + color: var(--top-nav-text-color); + background-color: unset; + + :global(.ak-icon) { + font-size: 1.5rem !important; + } + + &:hover { + color: var(--top-nav-text-color); + background-color: unset; + } + } + + .right-nav { + margin-left: auto; + } + + .navbar-title { + color: var(--top-nav-text-color); + } + + .spacer { + flex-grow: 1; + } + + .appknox-logo { + pointer-events: none; + padding: 1em 0; + + img { + max-width: 12em; + max-height: 2em; + } + } +} diff --git a/app/components/home-page/organization-dashboard/header/index.ts b/app/components/top-nav/index.ts similarity index 62% rename from app/components/home-page/organization-dashboard/header/index.ts rename to app/components/top-nav/index.ts index c70669d98..74d6e6dcf 100644 --- a/app/components/home-page/organization-dashboard/header/index.ts +++ b/app/components/top-nav/index.ts @@ -1,21 +1,30 @@ +import Component from '@glimmer/component'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; -import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; -import IntlService from 'ember-intl/services/intl'; +import type IntlService from 'ember-intl/services/intl'; -import UserModel from 'irene/models/user'; -import FreshdeskService from 'irene/services/freshdesk'; -import IntegrationService from 'irene/services/integration'; -import ConfigurationService from 'irene/services/configuration'; +import type UserModel from 'irene/models/user'; +import type FreshdeskService from 'irene/services/freshdesk'; +import type UserAuthService from 'irene/services/user-auth'; +import styles from './index.scss'; + +type DefaultBlock = { + classes: { + navbarBtn?: string; + }; +}; -export interface HomePageOrganizationDashboardHeaderSignature { +export interface TopNavSignature { Args: { - logoutAction: () => void; user: UserModel; - onToggleOnboardingGuide: () => void; + title?: string; + showNotifications?: boolean; }; Element: HTMLElement; + Blocks: { + default: [DefaultBlock]; + }; } interface ProfileMenuItem { @@ -26,24 +35,25 @@ interface ProfileMenuItem { isLast?: boolean; } -export default class HomePageOrganizationDashboardHeaderComponent extends Component { - @service declare integration: IntegrationService; +export default class TopNavComponent extends Component { @service declare freshdesk: FreshdeskService; @service declare intl: IntlService; - @service declare configuration: ConfigurationService; + @service declare userAuth: UserAuthService; @tracked profileAnchorRef: HTMLElement | null = null; - get showKnowledgeBase() { - return this.freshdesk.supportWidgetIsEnabled; + get classes() { + return { + navbarBtn: styles['navbar-btn'], + }; } get showChatSupport() { return this.freshdesk.freshchatEnabled; } - get orgIsAnEnterprise() { - return this.configuration.serverData.enterprise; + get showNotifications() { + return this.args.showNotifications ?? true; } get profileMenuItems() { @@ -73,7 +83,7 @@ export default class HomePageOrganizationDashboardHeaderComponent extends Compon @action handleLogoutClick() { - this.args.logoutAction(); + this.userAuth.invalidateSession(); this.handleProfileMenuClose(); } @@ -82,10 +92,6 @@ export default class HomePageOrganizationDashboardHeaderComponent extends Compon this.handleProfileMenuClose(); } - @action onOpenKnowledgeBase() { - this.freshdesk.openSupportWidget(); - } - @action handleProfileBtnClick(event: MouseEvent) { this.profileAnchorRef = event.currentTarget as HTMLElement; @@ -99,6 +105,6 @@ export default class HomePageOrganizationDashboardHeaderComponent extends Compon declare module '@glint/environment-ember-loose/registry' { export default interface Registry { - 'HomePage::OrganizationDashboard::Header': typeof HomePageOrganizationDashboardHeaderComponent; + TopNav: typeof TopNavComponent; } } diff --git a/app/router.ts b/app/router.ts index 938ecf568..09c6c34c6 100644 --- a/app/router.ts +++ b/app/router.ts @@ -43,6 +43,8 @@ Router.map(function () { path: '/', }); + this.route('home', { path: '/dashboard/home' }); + this.route('organization', function () { this.route('namespaces'); this.route('users'); @@ -130,6 +132,7 @@ Router.map(function () { this.route('analytics'); }); + // Appknox routes this.route('dashboard', function () { this.route('projects'); @@ -264,6 +267,19 @@ Router.map(function () { this.route('docs'); }); }); + + // Storeknox routes + this.route('storeknox', { path: '/dashboard/storeknox' }, function () { + this.route('index', { path: '/' }); + + this.route('discover', function () { + this.route('result'); + this.route('requested'); + this.route('review'); + }); + + this.route('inventory'); + }); } ); diff --git a/app/routes/authenticated/dashboard.ts b/app/routes/authenticated/dashboard.ts new file mode 100644 index 000000000..6d7c80139 --- /dev/null +++ b/app/routes/authenticated/dashboard.ts @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class AuthenticatedDashboardRoute extends Route {} diff --git a/app/routes/authenticated/home.ts b/app/routes/authenticated/home.ts new file mode 100644 index 000000000..0765805dd --- /dev/null +++ b/app/routes/authenticated/home.ts @@ -0,0 +1,16 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import type RouterService from '@ember/routing/router-service'; + +import type OrganizationService from 'irene/services/organization'; + +export default class HomeRoute extends Route { + @service declare organization: OrganizationService; + @service declare router: RouterService; + + beforeModel() { + if (!this.organization.selected?.features.app_monitoring) { + this.router.transitionTo('authenticated.dashboard.projects'); + } + } +} diff --git a/app/routes/authenticated/index.ts b/app/routes/authenticated/index.ts index 7ba69847f..9d239101d 100644 --- a/app/routes/authenticated/index.ts +++ b/app/routes/authenticated/index.ts @@ -6,6 +6,6 @@ export default class IndexRoute extends Route { @service declare router: RouterService; beforeModel() { - this.router.transitionTo('/projects'); + this.router.transitionTo('authenticated.home'); } } diff --git a/app/routes/authenticated/partner.ts b/app/routes/authenticated/partner.ts index 844064589..514907413 100644 --- a/app/routes/authenticated/partner.ts +++ b/app/routes/authenticated/partner.ts @@ -1,8 +1,9 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; -import MeService from 'irene/services/me'; -import PartnerService from 'irene/services/partner'; +import type MeService from 'irene/services/me'; +import type PartnerService from 'irene/services/partner'; +import type UserModel from 'irene/models/user'; export default class AuthenticatedPartnerRoute extends Route { @service declare me: MeService; @@ -11,5 +12,9 @@ export default class AuthenticatedPartnerRoute extends Route { async model() { // Load partner service await this.partner.load(); + + return { + user: this.modelFor('authenticated') as UserModel, + }; } } diff --git a/app/routes/authenticated/storeknox.ts b/app/routes/authenticated/storeknox.ts new file mode 100644 index 000000000..f08db247f --- /dev/null +++ b/app/routes/authenticated/storeknox.ts @@ -0,0 +1,16 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import type RouterService from '@ember/routing/router-service'; + +import type OrganizationService from 'irene/services/organization'; + +export default class AuthenticatedStoreknoxRoute extends Route { + @service declare organization: OrganizationService; + @service declare router: RouterService; + + beforeModel() { + if (!this.organization.selected?.features.app_monitoring) { + this.router.transitionTo('authenticated.dashboard.projects'); + } + } +} diff --git a/app/services/user-auth.ts b/app/services/user-auth.ts new file mode 100644 index 000000000..4989c3f96 --- /dev/null +++ b/app/services/user-auth.ts @@ -0,0 +1,19 @@ +import Service from '@ember/service'; +import { service } from '@ember/service'; +import { action } from '@ember/object'; + +import triggerAnalytics from 'irene/utils/trigger-analytics'; +import type FreshdeskService from './freshdesk'; + +export default class UserAuthService extends Service { + @service declare session: any; + @service declare freshdesk: FreshdeskService; + + @action invalidateSession() { + this.freshdesk.logUserOutOfSupportWidget(); + + triggerAnalytics('logout', {} as CsbAnalyticsData); + + this.session.invalidate(); + } +} diff --git a/app/styles/_component-variables.scss b/app/styles/_component-variables.scss index d44b99adb..9367601a5 100644 --- a/app/styles/_component-variables.scss +++ b/app/styles/_component-variables.scss @@ -326,80 +326,61 @@ body { } body { - //variables for home-page/organization-dashboard/header + //variables for top-nav &.theme-light { - --organization-dashboard-header-background-color: var(--background-main); - --organization-dashboard-header-text-color: var(--text-primary); - --organization-dashboard-header-security-dashboard-btn-color: var( + --top-nav-background-color: var(--background-main); + --top-nav-text-color: var(--text-primary); + --top-nav-security-dashboard-btn-color: var( --secondary-main ); } &.theme-dark { - --organization-dashboard-header-background-color: var(--secondary-main); - --organization-dashboard-header-text-color: var(--secondary-contrast-text); - --organization-dashboard-header-security-dashboard-btn-color: var( + --top-nav-background-color: var(--secondary-main); + --top-nav-text-color: var(--secondary-contrast-text); + --top-nav-security-dashboard-btn-color: var( --secondary-contrast-text ); } - //variables for home-page/organization-dashboard/side-nav - --organization-dashboard-side-nav-font-weight-medium: var( - --font-weight-medium - ); - --organization-dashboard-side-nav-text-primary: var(--primary-main); - --organization-dashboard-side-nav-text-secondary: var(--text-secondary); - --organization-dashboard-side-nav-item-active-badge-background: var( - --primary-main - ); - --organization-dashboard-side-nav-item-active-badge-text-color: var( - --primary-contrast-text - ); - --organization-dashboard-side-nav-item-active-border-color: var( - --primary-main - ); - --organization-dashboard-side-nav-zIndex: calc(var(--zIndex-appbar) + 1); - + //variables for side-nav + --side-nav-font-weight-medium: var(--font-weight-medium); + --side-nav-text-primary: var(--primary-main); + --side-nav-item-active-badge-background: var(--primary-main); + --side-nav-item-active-badge-text-color: var(--primary-contrast-text); + --side-nav-item-active-border-color: var(--primary-main); + --side-nav-zIndex: calc(var(--zIndex-appbar) + 1); + &.theme-light { - --organization-dashboard-side-nav-bg-color: var(--background-main); - --organization-dashboard-side-nav-text-color: var(--text-primary); - --organization-dashboard-side-nav-box-shadow: var(--box-shadow-light); - --organization-dashboard-side-nav-item-badge-background: var( - --neutral-grey-200 - ); - --organization-dashboard-side-nav-item-badge-text-color: var( - --text-primary - ); - --organization-dashboard-side-nav-item-hover-background: var( - --hover-light-background - ); - --organization-dashboard-side-nav-item-active-background: var( - --side-menu-active-background-light - ); - --organization-dashboard-side-nav-border: 1px solid var(--border-color-1); + --side-nav-inherit-background: var(--background-main); + --side-nav-inherit-background-text-color: var(--text-primary); + --side-nav-inherit-hover-background: var(--hover-light-background); + --side-nav-item-badge-background: var(--neutral-grey-200); + --side-nav-item-badge-text-color: var(--text-primary); + --side-nav-item-active-background: var(--side-menu-active-background-light); + --side-nav-box-shadow: var(--box-shadow-light); + --side-nav-border: 1px solid var(--border-color-1); } - + &.theme-dark { - --organization-dashboard-side-nav-bg-color: var(--background-dark); - --organization-dashboard-side-nav-text-color: var( - --secondary-contrast-text - ); - --organization-dashboard-side-nav-box-shadow: var(--box-shadow-dark); - --organization-dashboard-side-nav-item-badge-background: var( - --neutral-grey-900 - ); - --organization-dashboard-side-nav-item-badge-text-color: var( - --secondary-contrast-text - ); - --organization-dashboard-side-nav-item-hover-background: var( - --hover-dark-background - ); - --organization-dashboard-side-nav-item-active-background: var( - --side-menu-active-background-dark - ); - --organization-dashboard-side-nav-border: none; + --side-nav-inherit-background: var(--background-dark); + --side-nav-inherit-background-text-color: var(--common-white); + --side-nav-inherit-hover-background: var(--hover-dark-background); + --side-nav-item-badge-background: var(--neutral-grey-900); + --side-nav-item-badge-text-color: var(--secondary-contrast-text); + --side-nav-item-active-background: var(--side-menu-active-background-dark); + --side-nav-box-shadow: var(--box-shadow-dark); + --side-nav-border: none; } + //variables for side-nav/product-switcher + --side-nav-product-switcher-modal-item-hover-background: var(--neutral-grey-100); + --side-nav-product-switcher-modal-title-background: var(--neutral-grey-100); + --side-nav-product-switcher-border: 1px solid var(--border-color-1); + --side-nav-product-switcher-bg-main: var(--background-main); + --side-nav-product-switcher-box-shadow: var(--box-shadow-light); + --side-nav-product-switcher-item-hover-background: var(--hover-light-background); + // variables for organization-namespace/namespace-value --organization-namespace-value-color-ios: var(--ios); --organization-namespace-value-color-android: var(--android); @@ -1508,8 +1489,20 @@ body { --register-oidc-error-border-color: var(--border-color-1); --register-oidc-error-error-card-box-shadow: var(--box-shadow-2); - // variables for home-page/organization-dashboard/onboarding-guide - --onboarding-guide-list-border-color: var(--border-color-1); + // variables for appknox-wrapper + --appknox-wrapper-text-primary: var(--primary-main); + --appknox-wrapper-border-color: var(--border-color-1); + + &.theme-light { + --appknox-wrapper-text-color: var(--text-primary); + } + + &.theme-dark { + --appknox-wrapper-text-color: var(--secondary-contrast-text); + } + + // variables for appknox-wrapper/onboarding-guide + --appknox-wrapper-onboarding-guide-list-border-color: var(--border-color-1); // variables for marketplace --marketplace-sub-header-description-text-color: var(--text-secondary); @@ -1535,9 +1528,6 @@ body { --security-purge-api-analysis-container-box-shadow: var(--box-shadow-3); --security-purge-api-analysis-container-border-radius: var(--border-radius); - // variables for home-page/security-dashboard-nav - --security-dashboard-nav-bg-color: var(--background-main); - // variables for system-status --system-status-root-container-border-color: var(--neutral-grey-200); --system-status-root-container-box-shadow: var(--box-shadow-3); @@ -1922,4 +1912,13 @@ body { --file-details-dynamic-scan-drawer-old-proxy-settings-view-border-color: var( --border-color-1 ); + + // variables for home-page + --home-page-background-color: var(--background-light); + --home-page-button-background-color: var(--background-main); + + // variables for home-page/product-card + --home-page-product-card-border-radius: var(--border-radius); + --home-page-product-card-border-color: var(--border-color-1); + --home-page-product-card-background-color: var(--background-main); } diff --git a/app/styles/_icons.scss b/app/styles/_icons.scss index 367be42f2..dfa8a3164 100644 --- a/app/styles/_icons.scss +++ b/app/styles/_icons.scss @@ -578,3 +578,15 @@ .ak-icon-stop-circle { @extend .mi-stop-circle; } + +.ak-icon-location-searching { + @extend .mi-location-searching; +} + +.ak-icon-auto-graph { + @extend .mi-auto-graph; +} + +.ak-icon-north-east { + @extend .mi-north-east; +} diff --git a/app/styles/_theme.scss b/app/styles/_theme.scss index b9c4a442b..16293056f 100644 --- a/app/styles/_theme.scss +++ b/app/styles/_theme.scss @@ -90,7 +90,7 @@ --zIndex-snackbar: 1400; --zIndex-tooltip: 1500; - // special case variable for home-page/organization-dashboard/side-nav + // special case variable for side-nav --side-menu-active-background-dark: #303339; --side-menu-active-background-light: rgba(254, 77, 63, 0.1); diff --git a/app/templates/authenticated.hbs b/app/templates/authenticated.hbs index c1780ac7d..e2147cab0 100644 --- a/app/templates/authenticated.hbs +++ b/app/templates/authenticated.hbs @@ -1,3 +1 @@ - - {{outlet}} - \ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/app/templates/authenticated/dashboard.hbs b/app/templates/authenticated/dashboard.hbs new file mode 100644 index 000000000..9bc8e3312 --- /dev/null +++ b/app/templates/authenticated/dashboard.hbs @@ -0,0 +1,5 @@ +{{page-title (t 'vapt')}} + + + {{outlet}} + \ No newline at end of file diff --git a/app/templates/authenticated/home.hbs b/app/templates/authenticated/home.hbs new file mode 100644 index 000000000..a23e756ce --- /dev/null +++ b/app/templates/authenticated/home.hbs @@ -0,0 +1,3 @@ +{{page-title (t 'home')}} + + \ No newline at end of file diff --git a/app/templates/authenticated/partner.hbs b/app/templates/authenticated/partner.hbs index c41f2cf4f..faddb648d 100644 --- a/app/templates/authenticated/partner.hbs +++ b/app/templates/authenticated/partner.hbs @@ -1,3 +1,5 @@ -{{page-title "Partner"}} - -{{outlet}} \ No newline at end of file +{{page-title (t 'partner')}} + + + {{outlet}} + \ No newline at end of file diff --git a/app/templates/authenticated/security.hbs b/app/templates/authenticated/security.hbs index c24cd6895..3a49e696d 100644 --- a/app/templates/authenticated/security.hbs +++ b/app/templates/authenticated/security.hbs @@ -1 +1,5 @@ -{{outlet}} +{{page-title (t 'security')}} + + + {{outlet}} + \ No newline at end of file diff --git a/app/templates/authenticated/storeknox.hbs b/app/templates/authenticated/storeknox.hbs new file mode 100644 index 000000000..18bd230aa --- /dev/null +++ b/app/templates/authenticated/storeknox.hbs @@ -0,0 +1,5 @@ +{{page-title (t 'storeknox')}} + + + {{outlet}} + \ No newline at end of file diff --git a/app/templates/authenticated/storeknox/discover.hbs b/app/templates/authenticated/storeknox/discover.hbs new file mode 100644 index 000000000..e69de29bb diff --git a/app/templates/authenticated/storeknox/inventory.hbs b/app/templates/authenticated/storeknox/inventory.hbs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/app/templates/authenticated/storeknox/inventory.hbs @@ -0,0 +1 @@ + diff --git a/app/utils/scroll-to-top.ts b/app/utils/scroll-to-top.ts index 67f0a64c9..2d2428d55 100644 --- a/app/utils/scroll-to-top.ts +++ b/app/utils/scroll-to-top.ts @@ -4,14 +4,12 @@ import Transition from '@ember/routing/transition'; // Route constructor type Constructor = new (...args: (object | undefined)[]) => T; -export const scrollOrganizationDashboardMainContainerTo = ( - options?: ScrollToOptions -) => { - const organizationDashboardMainContainer = document.querySelector( - '#ak-organization-dashboard-main' +export const scrollDashboardMainContainerTo = (options?: ScrollToOptions) => { + const akDashboardMainContainer = document.querySelector( + '#ak-dashboard-main-scroll-container' ); - organizationDashboardMainContainer?.scrollTo(options); + akDashboardMainContainer?.scrollTo(options); }; /** @@ -29,7 +27,7 @@ export const ScrollToTop = ( activate(_transition: Transition) { super.activate(_transition); - scrollOrganizationDashboardMainContainerTo({ top: 0, left: 0 }); + scrollDashboardMainContainerTo({ top: 0, left: 0 }); window.scrollTo(0, 0); } diff --git a/package-lock.json b/package-lock.json index da2ebf3f3..038a8c9b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -366,45 +366,6 @@ "node": ">=0.4.0" } }, - "node_modules/@appknox/ember-pace/node_modules/babel-core": { - "version": "6.26.3", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "node_modules/@appknox/ember-pace/node_modules/babel-core/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@appknox/ember-pace/node_modules/babel-core/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/@appknox/ember-pace/node_modules/babel-import-util": { "version": "1.4.1", "dev": true, @@ -513,11 +474,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@appknox/ember-pace/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, "node_modules/@appknox/ember-pace/node_modules/debug": { "version": "3.2.7", "dev": true, @@ -883,9 +839,13 @@ "license": "MIT" }, "node_modules/@appknox/ember-pace/node_modules/json5": { - "version": "0.5.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, "bin": { "json5": "lib/cli.js" } @@ -1165,16 +1125,10 @@ "randombytes": "^2.1.0" } }, - "node_modules/@appknox/ember-pace/node_modules/slash": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@appknox/ember-pace/node_modules/source-map": { - "version": "0.5.7", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1334,22 +1288,6 @@ "semver": "bin/semver" } }, - "node_modules/@appknox/ember-pace/node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@appknox/ember-pace/node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@appknox/ember-pace/node_modules/tmp": { "version": "0.0.33", "dev": true, @@ -1451,25 +1389,6 @@ "source-map": "~0.6.1" } }, - "node_modules/@appknox/ember-pace/node_modules/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@appknox/ember-pace/node_modules/webpack/node_modules/json5": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/@appknox/ember-pace/node_modules/webpack/node_modules/loader-utils": { "version": "1.4.2", "dev": true, @@ -7458,7 +7377,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "license": "MIT", "engines": { @@ -9275,16 +9196,17 @@ } }, "node_modules/@storybook/addon-docs": { - "version": "8.2.9", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.3.0.tgz", + "integrity": "sha512-LrvWBDX5Vi//82Q78QRbTsG+9rJU9JJFAVPk1NnLp2Yn0F4FueVzIw8AabAkZFy0LHPMGV+EHpkPtYz4Czkhgw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.24.4", "@mdx-js/react": "^3.0.0", - "@storybook/blocks": "8.2.9", - "@storybook/csf-plugin": "8.2.9", + "@storybook/blocks": "8.3.0", + "@storybook/csf-plugin": "8.3.0", "@storybook/global": "^5.0.0", - "@storybook/react-dom-shim": "8.2.9", + "@storybook/react-dom-shim": "8.3.0", "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "fs-extra": "^11.1.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", @@ -9298,7 +9220,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.2.9" + "storybook": "^8.3.0" } }, "node_modules/@storybook/addon-docs/node_modules/fs-extra": { @@ -9640,13 +9562,15 @@ } }, "node_modules/@storybook/blocks": { - "version": "8.2.9", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.3.0.tgz", + "integrity": "sha512-V7D5lv5R+GJya9cCZOCjmOVjhvP5J3KIaclQuuGGJda/ZD/SpwHcFOGSpo6sNR2UKHXXvb61oM8gRQQWDvqPlg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "@storybook/global": "^5.0.0", - "@storybook/icons": "^1.2.5", + "@storybook/icons": "^1.2.10", "@types/lodash": "^4.14.167", "color-convert": "^2.0.1", "dequal": "^2.0.2", @@ -9666,7 +9590,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.2.9" + "storybook": "^8.3.0" }, "peerDependenciesMeta": { "react": { @@ -11120,7 +11044,9 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "8.2.9", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.3.0.tgz", + "integrity": "sha512-sCmeN/OVYj95TKkMqJqxbaztIbdv5jCrtrXuNg4oJaGzNucmMNAbmv2jK2tCNE6Uz2X9IMRcseFX/h9TgjyJ9A==", "dev": true, "license": "MIT", "dependencies": { @@ -11131,7 +11057,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.2.9" + "storybook": "^8.3.0" } }, "node_modules/@storybook/ember": { @@ -12194,7 +12120,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "8.2.9", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.3.0.tgz", + "integrity": "sha512-87X4cvgwFT1ll5SzXgQq6iGbkVCgxLBpBm58akF/hzpeRkwfJDncGi/A5hElOJrBg63IkznmSJE7tf9RkrboqQ==", "dev": true, "license": "MIT", "funding": { @@ -12204,7 +12132,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.2.9" + "storybook": "^8.3.0" } }, "node_modules/@storybook/router": { @@ -12356,14 +12284,15 @@ } }, "node_modules/@storybook/types": { - "version": "8.2.9", - "license": "MIT", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-8.3.0.tgz", + "integrity": "sha512-2lF3pac9ktjUD85uPQBAxfNwbch4Vr56HB1Vnq7mLDIkV+OiLn9jEZ4Oabpq/y8jWYmGY5giRru0dIlZ+iPQuQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.2.9" + "storybook": "^8.3.0" } }, "node_modules/@storybook/ui": { @@ -14021,8 +13950,9 @@ "license": "MIT" }, "node_modules/@types/qs": { - "version": "6.9.15", - "license": "MIT" + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==" }, "node_modules/@types/qunit": { "version": "2.19.10", @@ -14134,22 +14064,26 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", + "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/type-utils": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^5.2.4", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -14165,19 +14099,33 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -14193,15 +14141,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -14209,17 +14159,19 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", + "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/utils": "7.2.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -14235,11 +14187,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.18.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -14247,21 +14201,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -14282,7 +14238,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "license": "ISC", "dependencies": { @@ -14307,17 +14265,22 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "semver": "^7.5.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -14327,16 +14290,29 @@ "eslint": "^8.56.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "7.2.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -15700,6 +15676,81 @@ "node": ">=0.8.0" } }, + "node_modules/babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "node_modules/babel-core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/babel-core/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/babel-core/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/babel-core/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/babel-core/node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/babel-generator": { "version": "6.26.1", "dev": true, @@ -16595,59 +16646,12 @@ "source-map-support": "^0.4.15" } }, - "node_modules/babel-register/node_modules/babel-core": { - "version": "6.26.3", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "node_modules/babel-register/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, "node_modules/babel-register/node_modules/core-js": { "version": "2.6.12", "dev": true, "hasInstallScript": true, "license": "MIT" }, - "node_modules/babel-register/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/babel-register/node_modules/json5": { - "version": "0.5.1", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/babel-register/node_modules/mkdirp": { "version": "0.5.6", "dev": true, @@ -16659,19 +16663,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/babel-register/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/babel-register/node_modules/slash": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/babel-register/node_modules/source-map": { "version": "0.5.7", "dev": true, @@ -19379,11 +19370,6 @@ "y18n": "^4.0.0" } }, - "node_modules/cacache/node_modules/chownr": { - "version": "1.1.4", - "dev": true, - "license": "ISC" - }, "node_modules/cacache/node_modules/mkdirp": { "version": "0.5.6", "dev": true, @@ -19784,8 +19770,16 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "devOptional": true + }, "node_modules/chromatic": { - "version": "11.7.1", + "version": "11.10.2", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.10.2.tgz", + "integrity": "sha512-EbVlhmOLGdx9QRX3RMOTF3UzoyC1aaXNRjlzm1mc++2OI5+6C5Bzwt2ZUYJ3Jnf/pJa23q0y5Y3QEDcfRVqIbg==", "dev": true, "license": "MIT", "bin": { @@ -22008,8 +22002,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.18", - "license": "ISC" + "version": "1.5.23", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.23.tgz", + "integrity": "sha512-mBhODedOXg4v5QWwl21DjM5amzjmI1zw9EPrPK/5Wx7C8jt33bpZNrC7OhHUG3pxRtbLpr3W2dXT+Ph1SsfRZA==" }, "node_modules/elliptic": { "version": "6.5.7", @@ -25793,32 +25788,6 @@ "ensure-posix-path": "^1.0.1" } }, - "node_modules/ember-cli-bourbon/node_modules/babel-core": { - "version": "6.26.3", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, "node_modules/ember-cli-bourbon/node_modules/babel-plugin-debug-macros": { "version": "0.2.0", "dev": true, @@ -25878,19 +25847,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ember-cli-bourbon/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, - "node_modules/ember-cli-bourbon/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, "node_modules/ember-cli-bourbon/node_modules/ember-cli-babel": { "version": "6.18.0", "dev": true, @@ -25926,14 +25882,6 @@ "node": ">= 4" } }, - "node_modules/ember-cli-bourbon/node_modules/json5": { - "version": "0.5.1", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/ember-cli-bourbon/node_modules/merge-trees": { "version": "1.0.1", "dev": true, @@ -25947,11 +25895,6 @@ "symlink-or-copy": "^1.0.0" } }, - "node_modules/ember-cli-bourbon/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/ember-cli-bourbon/node_modules/rsvp": { "version": "4.8.5", "dev": true, @@ -25968,22 +25911,6 @@ "semver": "bin/semver" } }, - "node_modules/ember-cli-bourbon/node_modules/slash": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ember-cli-bourbon/node_modules/source-map": { - "version": "0.5.7", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ember-cli-bourbon/node_modules/workerpool": { "version": "2.3.4", "dev": true, @@ -35075,32 +35002,6 @@ "ensure-posix-path": "^1.0.1" } }, - "node_modules/ember-maybe-import-regenerator/node_modules/babel-core": { - "version": "6.26.3", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, "node_modules/ember-maybe-import-regenerator/node_modules/babel-plugin-debug-macros": { "version": "0.2.0", "dev": true, @@ -35219,11 +35120,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ember-maybe-import-regenerator/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, "node_modules/ember-maybe-import-regenerator/node_modules/debug": { "version": "2.6.9", "dev": true, @@ -35290,14 +35186,6 @@ "node": ">= 4" } }, - "node_modules/ember-maybe-import-regenerator/node_modules/json5": { - "version": "0.5.1", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/ember-maybe-import-regenerator/node_modules/merge-trees": { "version": "1.0.1", "dev": true, @@ -35348,22 +35236,6 @@ "semver": "bin/semver" } }, - "node_modules/ember-maybe-import-regenerator/node_modules/slash": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ember-maybe-import-regenerator/node_modules/source-map": { - "version": "0.5.7", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ember-maybe-import-regenerator/node_modules/workerpool": { "version": "2.3.4", "dev": true, @@ -37207,45 +37079,6 @@ "node": ">=0.4.0" } }, - "node_modules/ember-rollbar-client/node_modules/babel-core": { - "version": "6.26.3", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "node_modules/ember-rollbar-client/node_modules/babel-core/node_modules/debug": { - "version": "2.6.9", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/ember-rollbar-client/node_modules/babel-core/node_modules/ms": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/ember-rollbar-client/node_modules/babel-import-util": { "version": "1.4.1", "dev": true, @@ -37439,11 +37272,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ember-rollbar-client/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, "node_modules/ember-rollbar-client/node_modules/debug": { "version": "3.2.7", "dev": true, @@ -37883,9 +37711,13 @@ "license": "MIT" }, "node_modules/ember-rollbar-client/node_modules/json5": { - "version": "0.5.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, "bin": { "json5": "lib/cli.js" } @@ -38146,16 +37978,10 @@ "randombytes": "^2.1.0" } }, - "node_modules/ember-rollbar-client/node_modules/slash": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ember-rollbar-client/node_modules/source-map": { - "version": "0.5.7", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -38339,22 +38165,6 @@ "semver": "bin/semver" } }, - "node_modules/ember-rollbar-client/node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ember-rollbar-client/node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ember-rollbar-client/node_modules/tmp": { "version": "0.0.33", "dev": true, @@ -38456,25 +38266,6 @@ "source-map": "~0.6.1" } }, - "node_modules/ember-rollbar-client/node_modules/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ember-rollbar-client/node_modules/webpack/node_modules/json5": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/ember-rollbar-client/node_modules/webpack/node_modules/loader-utils": { "version": "1.4.2", "dev": true, @@ -44915,7 +44706,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.8.0", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, "license": "MIT", "dependencies": { @@ -48451,13 +48244,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "5.0.0", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, "node_modules/miragejs": { "version": "0.1.48", "dev": true, @@ -52646,6 +52432,14 @@ "version": "10.4.3", "license": "ISC" }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-to-regexp": { "version": "0.1.10", "license": "MIT" @@ -52878,7 +52672,9 @@ } }, "node_modules/postcss": { - "version": "8.4.45", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -52896,8 +52692,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -53386,8 +53182,9 @@ "license": "MIT" }, "node_modules/pump": { - "version": "3.0.0", - "license": "MIT", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -53989,8 +53786,9 @@ "license": "MIT" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "license": "MIT", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dependencies": { "regenerate": "^1.4.2" }, @@ -56897,7 +56695,9 @@ } }, "node_modules/stylelint-scss": { - "version": "6.5.1", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.6.0.tgz", + "integrity": "sha512-aK2Rdt41Jt9Gv/ClMN5BYhP7xR3IEoRMDKpJkIDI9frZQ6KkxeLishusxs2JtEGZdJWXvPoBOhswNxj3gx8L/g==", "dev": true, "license": "MIT", "dependencies": { @@ -56905,8 +56705,8 @@ "is-plain-object": "5.0.0", "known-css-properties": "^0.34.0", "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.4", - "postcss-selector-parser": "^6.1.1", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-selector-parser": "^6.1.2", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -57184,9 +56984,9 @@ "dev": true }, "node_modules/swagger-client": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.29.4.tgz", - "integrity": "sha512-Me8tdPyRAQbnwNBCZ0BpG0vyci9e+FW6YV3+c6/x8SwPmLpslpFNXoT4PtVApf1CVSvV7Sc7Bfb4DPgpEqBdHw==", + "version": "3.29.3", + "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.29.3.tgz", + "integrity": "sha512-OhhMAO2dwDEaxtUNDxwaqzw75uiZY5lX/2vx+U6eKCYZYhXWQ5mylU/0qfk/xMR20VyitsnzRc6KcFFjRoCS7A==", "dependencies": { "@babel/runtime-corejs3": "^7.22.15", "@swagger-api/apidom-core": ">=1.0.0-alpha.9 <1.0.0-beta.0", @@ -57209,7 +57009,8 @@ }, "node_modules/swagger-client/node_modules/ramda": { "version": "0.30.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz", + "integrity": "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/ramda" @@ -57508,11 +57309,6 @@ "tar-stream": "^2.1.4" } }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "license": "ISC", - "optional": true - }, "node_modules/tar-stream": { "version": "2.2.0", "license": "MIT", @@ -57673,7 +57469,9 @@ } }, "node_modules/testem": { - "version": "3.15.1", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/testem/-/testem-3.15.2.tgz", + "integrity": "sha512-mRzqZktqTCWi/rUP/RQOKXvMtuvY3lxuzBVb1xGXPnRNGMEj/1DaLGn6X447yOsz6SlWxSsZfcNuiE7fT1MOKg==", "dev": true, "license": "MIT", "dependencies": { @@ -58575,8 +58373,9 @@ "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "license": "MIT", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "engines": { "node": ">=4" } @@ -58593,8 +58392,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "license": "MIT", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "engines": { "node": ">=4" } @@ -58714,7 +58514,9 @@ } }, "node_modules/unplugin": { - "version": "1.14.0", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.14.1.tgz", + "integrity": "sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==", "dev": true, "license": "MIT", "dependencies": { @@ -59337,6 +59139,8 @@ }, "node_modules/watchpack-chokidar2/node_modules/chokidar": { "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "license": "MIT", "optional": true, diff --git a/tests/acceptance/home-page-test.js b/tests/acceptance/home-page-test.js new file mode 100644 index 000000000..bea0f0cce --- /dev/null +++ b/tests/acceptance/home-page-test.js @@ -0,0 +1,148 @@ +import { module, test } from 'qunit'; + +import { click, currentURL, visit } from '@ember/test-helpers'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { t } from 'ember-intl/test-support'; +import { setupApplicationTest } from 'ember-qunit'; +import Service from '@ember/service'; + +import { setupRequiredEndpoints } from '../helpers/acceptance-utils'; + +class IntegrationStub extends Service { + async configure(user) { + this.currentUser = user; + } + + isPendoEnabled() { + return false; + } + + isCrispEnabled() { + return false; + } +} + +class WebsocketStub extends Service { + async connect() {} + + async configure() {} +} + +module('Acceptance | home page', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(async function () { + const { organization } = await setupRequiredEndpoints(this.server); + + this.owner.register('service:integration', IntegrationStub); + this.owner.register('service:websocket', WebsocketStub); + + organization.update({ + features: { + app_monitoring: true, + }, + }); + + this.setProperties({ + organization, + }); + }); + + test('it renders', async function (assert) { + await visit('/dashboard/home'); + + assert.dom('[data-test-home-page]').exists(); + + assert + .dom('[data-test-home-page-title]') + .exists() + .hasText(t('toSecureYourMobileApps')); + + assert.dom('[data-test-home-page-appLogo]').exists(); + + assert + .dom('[data-test-home-page-logoutBtn]') + .isNotDisabled() + .hasText(t('logout')); + + assert.dom('[data-test-home-page-product-card]').exists({ + count: 2, + }); + }); + + test('it redirects to appknox dashboard', async function (assert) { + await visit('/dashboard/home'); + + assert.dom('[data-test-home-page-product-card]').exists({ + count: 2, + }); + + assert.dom('[data-test-home-page-product-card-title]').exists({ count: 2 }); + + const titles = this.element.querySelectorAll( + '[data-test-home-page-product-card-title]' + ); + + assert.strictEqual(titles[0].textContent.trim(), t('appknox')); + assert.strictEqual(titles[1].textContent.trim(), t('storeknox')); + + const links = this.element.querySelectorAll( + '[data-test-home-page-product-card-link]' + ); + + assert + .dom('[data-test-home-page-product-card-indicator-icon]') + .exists({ count: 2 }); + + await click(links[0]); + + assert.strictEqual( + currentURL(), + '/dashboard/projects', + 'Redirected to appknox' + ); + }); + + test('it redirects to storeknox dashboard', async function (assert) { + await visit('/dashboard/home'); + + assert.dom('[data-test-home-page-product-card]').exists({ + count: 2, + }); + + const links = this.element.querySelectorAll( + '[data-test-home-page-product-card-link]' + ); + + assert + .dom('[data-test-home-page-product-card-indicator-icon]') + .exists({ count: 2 }); + + await click(links[1]); + + assert.strictEqual( + currentURL(), + '/dashboard/storeknox/discover', + 'Redirected to storeknox' + ); + }); + + test('it redirects to appknox dashboard if app monitoring is disabled', async function (assert) { + this.organization.update({ + features: { + app_monitoring: false, + }, + }); + + await visit('/dashboard/home'); + + assert.strictEqual( + currentURL(), + '/dashboard/projects', + 'Redirected to appknox' + ); + + assert.dom('[data-test-side-menu-switcher]').doesNotExist(); + }); +}); diff --git a/tests/acceptance/side-nav-test.js b/tests/acceptance/side-nav-test.js new file mode 100644 index 000000000..9f405bbec --- /dev/null +++ b/tests/acceptance/side-nav-test.js @@ -0,0 +1,159 @@ +import { module, test } from 'qunit'; + +import { click, currentURL, visit } from '@ember/test-helpers'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { setupApplicationTest } from 'ember-qunit'; +import Service from '@ember/service'; +import { setupBrowserFakes } from 'ember-browser-services/test-support'; + +import { setupRequiredEndpoints } from '../helpers/acceptance-utils'; + +class IntegrationStub extends Service { + async configure(user) { + this.currentUser = user; + } + + isPendoEnabled() { + return false; + } + + isCrispEnabled() { + return false; + } +} + +class WebsocketStub extends Service { + async connect() {} + + async configure() {} +} + +module('Acceptance | side nav test', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + setupBrowserFakes(hooks, { window: true }); + + hooks.beforeEach(async function () { + const { organization } = await setupRequiredEndpoints(this.server); + + this.owner.register('service:integration', IntegrationStub); + this.owner.register('service:websocket', WebsocketStub); + + const menuItems = [ + { label: 'Store Monitoring', url: '/dashboard/store-monitoring' }, + { label: 'SBOM', url: '/dashboard/sbom/apps?app_offset=0' }, + { label: 'Analytics', url: '/dashboard/analytics' }, + { label: 'Organization', url: '/dashboard/organization/namespaces' }, + { label: 'API Documentation', url: '/dashboard/public-api/docs' }, + { label: 'Account Settings', url: '/dashboard/settings/general' }, + { label: 'Billing', url: '/dashboard/billing' }, + ]; + + const notify = this.owner.lookup('service:notifications'); + + notify.setDefaultClearDuration(0); + + organization.update({ + features: { + app_monitoring: true, + sbom: true, + public_apis: true, + }, + }); + + this.setProperties({ + organization, + menuItems, + }); + }); + + test('it renders', async function (assert) { + await visit('/dashboard/projects'); + + assert.dom('[data-test-side-nav]').exists(); + + assert.dom('[data-test-img-logo]').exists(); + }); + + test('it should show the correct page on click of menu items', async function (assert) { + assert.expect(14); + + await visit('/dashboard/projects'); + + this.server.get('organizations/:id/am_configuration', (schema, req) => { + return { id: 1, enabled: false, organization: req.params.id }; + }); + + this.server.get('organizations/:id/scancount', () => { + return { count: 0, next: null, previous: null, results: [] }; + }); + + this.server.get('organizations/:id/recent_issues', () => { + return { count: 0, next: null, previous: null, results: [] }; + }); + + this.server.get('organizations/:id/appscan', () => { + return { count: 0, next: null, previous: null, results: [] }; + }); + + this.server.get('organizations/:id/invoices', () => { + return { count: 0, next: null, previous: null, results: [] }; + }); + + this.server.get('organizations/:id/subscriptions', () => { + return { count: 0, next: null, previous: null, results: [] }; + }); + + this.server.get('organizations/:id/plans', () => { + return { count: 0, next: null, previous: null, results: [] }; + }); + + for (const item of this.menuItems) { + assert.dom(`[data-test-side-menu-item="${item.label}"]`).exists(); + + await click(`[data-test-side-menu-item="${item.label}"] a`); + + assert.strictEqual(currentURL(), item.url); + } + }); + + test.each( + 'it should switch between VAPT and Store Monitoring', + [ + { + expectedUrl: '/dashboard/storeknox/discover', + visitUrl: '/dashboard/projects', + icon: 'sm-svg', + }, + { + expectedUrl: '/dashboard/projects', + visitUrl: '/dashboard/storeknox/discover', + icon: 'vp-svg', + }, + ], + + async function (assert, details) { + const window = this.owner.lookup('service:browser/window'); + + window.location.href = details.visitUrl; + + await visit(details.visitUrl); + + assert.dom('[data-test-side-menu-switcher]').exists(); + + await click('[data-test-side-menu-switcher]'); + + assert.dom('[data-test-side-menu-switcher-modal]').exists(); + + assert.dom('[data-test-switcher-popover-item-link]').exists(); + + assert + .dom(`[data-test-side-menu-switcher-modal="${details.icon}"]`) + .exists(); + + await click('[data-test-switcher-popover-item-link]'); + + assert.strictEqual(currentURL(), details.expectedUrl); + } + ); +}); diff --git a/tests/integration/components/appknox-wrapper-test.js b/tests/integration/components/appknox-wrapper-test.js new file mode 100644 index 000000000..9c1602065 --- /dev/null +++ b/tests/integration/components/appknox-wrapper-test.js @@ -0,0 +1,414 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { setupIntl, t } from 'ember-intl/test-support'; +import { click, find, findAll, render } from '@ember/test-helpers'; +import { setupBrowserFakes } from 'ember-browser-services/test-support'; +import { hbs } from 'ember-cli-htmlbars'; +import Service from '@ember/service'; + +import ENUMS from 'irene/enums'; +import ENV from 'irene/config/environment'; + +class NotificationsStub extends Service { + errorMsg = null; + successMsg = null; + + error(msg) { + this.errorMsg = msg; + } + success(msg) { + this.successMsg = msg; + } +} + +class FreshdeskStub extends Service { + supportWidgetIsEnabled = false; + freshchatEnabled = true; +} + +class ConfigurationStub extends Service { + frontendData = {}; + themeData = {}; + imageData = {}; + serverData = { urlUploadAllowed: true, enterprise: false }; +} + +class IntegrationStub extends Service { + async configure(user) { + this.currentUser = user; + } + + isPendoEnabled() { + return false; + } + + isCrispEnabled() { + return true; + } +} + +const menuItems = ({ + appMonitoring, + sbom, + analytics, + publicApis, + market, + billing, + partner, + security, +}) => + [ + { + label: t('allProjects'), + icon: 'folder', + hasBadge: true, + }, + appMonitoring && { label: t('appMonitoring'), icon: 'inventory-2' }, + sbom && { label: t('SBOM'), icon: 'receipt-long' }, + analytics && { label: t('analytics'), icon: 'graphic-eq' }, + { label: t('organization'), icon: 'people' }, + publicApis && { label: t('apiDocumentation') }, + { label: t('accountSettings'), icon: 'account-box' }, + market && { label: t('marketplace'), icon: 'account-balance' }, + billing && { label: t('billing'), icon: 'credit-card' }, + partner && { + label: t('clients'), + icon: 'groups-2', + hasBadge: true, + badgeLabel: t('beta'), + }, + security && { label: t('securityDashboard'), icon: 'security' }, + ].filter(Boolean); + +const sections = (enabled) => ({ + billing: enabled, + appMonitoring: enabled, + market: enabled, + partner: enabled, + security: enabled, + sbom: enabled, + publicApis: enabled, + analytics: enabled, +}); + +const lowerMenuItems = [ + { + title: () => t('chatSupport'), + icon: 'chat-bubble', + enabled: false, + }, + { + title: () => t('version'), + icon: 'info', + enabled: false, + }, + { + title: () => t('collapse'), + icon: 'keyboard-tab', + }, +]; + +module('Integration | Component | appknox-wrapper', function (hooks) { + setupRenderingTest(hooks); + setupMirage(hooks); + setupIntl(hooks); + setupBrowserFakes(hooks, { window: true, localStorage: true }); + + hooks.beforeEach(async function () { + this.server.createList('organization', 1); + + const store = this.owner.lookup('service:store'); + + const organizationMe = store.createRecord('organization-me', { + is_owner: true, + is_admin: true, + }); + + class OrganizationMeStub extends Service { + org = organizationMe; + } + + await this.owner.lookup('service:organization').load(); + + this.owner.register('service:me', OrganizationMeStub); + this.owner.register('service:notifications', NotificationsStub); + this.owner.register('service:freshdesk', FreshdeskStub); + this.owner.register('service:integration', IntegrationStub); + this.owner.register('service:configuration', ConfigurationStub); + + const organization = this.owner.lookup('service:organization'); + + const window = this.owner.lookup('service:browser/window'); + + window.localStorage.clear(); + + this.setProperties({ + title: t('vapt'), + organization: organization, + user: store.createRecord('user', this.server.create('user').toJSON()), + }); + + // handle submissions api for each test + this.server.get('/submissions', () => []); + }); + + test.each( + 'test top nav and upload app status', + [{ knowledgeBase: true, hasUploadAppStatus: true }, {}], + async function (assert, { knowledgeBase, hasUploadAppStatus }) { + const freshdesk = this.owner.lookup('service:freshdesk'); + + freshdesk.supportWidgetIsEnabled = knowledgeBase; + + if (hasUploadAppStatus) { + this.server.createList('submission', 2, { + status: ENUMS.SUBMISSION_STATUS.VALIDATING, + }); + + this.server.get('/submissions', (schema) => { + return schema.submissions.all().models; + }); + } + + await render(hbs` + + `); + + assert.dom('[data-test-topNav]').exists(); + + assert.dom('[data-test-topNav-title]').hasText(this.title); + + assert + .dom('[data-test-topNav-startScanLabel]') + .hasText(t('startNewScan')); + + assert.dom('[data-test-uploadApp-input]').exists(); + + assert.dom('[data-test-uploadApp-uploadBtn]').hasText(t('uploadApp')); + assert.dom('[data-test-uploadAppViaLink-btn]').isNotDisabled(); + + if (hasUploadAppStatus) { + assert.dom('[data-test-uploadAppStatus-loader]').exists(); + assert.dom('[data-test-uploadAppStatus-icon]').exists(); + } else { + assert.dom('[data-test-uploadAppStatus-loader]').doesNotExist(); + assert.dom('[data-test-uploadAppStatus-icon]').doesNotExist(); + } + + assert.dom('[data-test-topNav-OnboardingGuideBtn]').exists(); + + if (knowledgeBase) { + assert + .dom('[data-test-topNav-KnowledgeBaseBtn]') + .isNotDisabled() + .hasText(t('knowledgeBase')); + } else { + assert.dom('[data-test-topNav-KnowledgeBaseBtn]').doesNotExist(); + } + } + ); + + test('test knowledge base click', async function (assert) { + assert.expect(5); + + const freshdesk = this.owner.lookup('service:freshdesk'); + + freshdesk.supportWidgetIsEnabled = true; + + freshdesk.openSupportWidget = function () { + assert.ok('Knowledge base clicked'); + assert.strictEqual(arguments.length, 0); + }; + + await render(hbs` + + `); + + assert.dom('[data-test-topNav]').exists(); + + assert + .dom('[data-test-topNav-KnowledgeBaseBtn]') + .isNotDisabled() + .hasText(t('knowledgeBase')); + + await click('[data-test-topNav-KnowledgeBaseBtn]'); + }); + + test.each( + 'It should not show onboarding guide for enterprise', + ['true', 'false'], + async function (assert, isEnterprise) { + const configuration = this.owner.lookup('service:configuration'); + configuration.serverData.enterprise = isEnterprise; + + await render(hbs` + + `); + + if (isEnterprise) { + assert.dom('[data-test-topNav-OnboardingGuideBtn]').doesNotExist(); + } else { + assert.dom('[data-test-topNav-OnboardingGuideBtn]').exists(); + + await click('[data-test-topNav-OnboardingGuideBtn]'); + + assert.dom('[data-test-onboarding-guide-modal]').exists(); + + await click('[data-test-topNav-OnboardingGuideBtn]'); + + assert.dom('[data-test-onboarding-guide-modal]').doesNotExist(); + } + } + ); + + test.each( + 'it should render appknox side nav', + [ + { owner: true, admin: true, ...sections(true) }, + { owner: true, admin: true, ...sections(false) }, + { owner: false, admin: true, ...sections(true) }, + { owner: false, admin: true, ...sections(false) }, + { owner: false, admin: false, ...sections(true) }, + { owner: false, admin: false, ...sections(false) }, + ], + async function (assert, scenario) { + const { owner, admin, ...sectionConfig } = scenario; + const me = this.owner.lookup('service:me'); + + me.org.is_owner = owner; + me.org.is_admin = admin; + me.org.can_access_partner_dashboard = sectionConfig.partner; + + ENV.enableMarketplace = sectionConfig.market; + + this.organization.selected.billingHidden = !sectionConfig.billing; + + this.organization.selected.features = { + app_monitoring: sectionConfig.appMonitoring, + public_apis: sectionConfig.publicApis, + sbom: sectionConfig.sbom, + }; + + this.server.get('/hudson-api/projects', () => { + if (sectionConfig.security) { + return new Response(200); + } else { + return new Response(404); + } + }); + + await render(hbs``); + + assert.dom('[data-test-img-logo]').exists(); + + const menuItemEle = findAll('[data-test-side-menu-item]'); + + assert.dom('[data-test-side-lower-menu]').exists(); + + assert.dom('[data-test-side-lower-menu-divider]').exists(); + + const expandButton = find(`[ + data-test-side-lower-menu-item="${t('expand')}" + ]`); + + await click(expandButton); + + menuItems({ + ...sectionConfig, + analytics: owner || admin, + billing: owner && sectionConfig.billing, + }).forEach((it, index) => { + if (it.icon) { + assert + .dom('[data-test-side-menu-item-icon]', menuItemEle[index]) + .hasClass(`ak-icon-${it.icon}`); + } + + assert + .dom('[data-test-side-menu-item-text]', menuItemEle[index]) + .hasText(it.label); + + if (it.hasBadge) { + assert + .dom('[data-test-side-menu-item-badge]', menuItemEle[index]) + .exists() + .hasText( + it.badgeLabel || String(this.organization.selected.projectsCount) + ); + } + }); + + const lowerMenuItemEle = findAll('[data-test-side-lower-menu-item]'); + + lowerMenuItems.forEach((it, index) => { + assert + .dom('[data-test-side-lower-menu-item-text]', lowerMenuItemEle[index]) + .containsText(it.title()); + + assert + .dom('[data-test-side-lower-menu-item-icon]', lowerMenuItemEle[index]) + .hasClass(`ak-icon-${it.icon}`); + }); + + const collapseButton = find(`[ + data-test-side-lower-menu-item="${t('collapse')}" + ]`); + + assert.ok(collapseButton, 'Collapse button should exist'); + + await click(collapseButton); + + assert.dom('[data-test-side-menu-item-text]').doesNotExist(); + + assert.dom('[data-test-side-lower-menu-item-text]').doesNotExist(); + } + ); + + test.each( + 'it should render switcher modal', + [true, false], + async function (assert, storeknox) { + this.organization.selected.features = { + app_monitoring: storeknox, + }; + + await render(hbs` + + `); + + const expandButton = find(`[ + data-test-side-lower-menu-item="${t('expand')}" + ]`); + + await click(expandButton); + + if (storeknox) { + assert.dom('[data-test-side-menu-switcher]').exists(); + + assert.dom('[data-test-side-menu-switcher-modal]').doesNotExist(); + + assert.dom('[data-test-side-menu-switcher-icon]').exists(); + + assert + .dom('[data-test-side-menu-switcher-text]') + .exists() + .hasText(t('appSwitcher')); + + await click('[data-test-side-menu-switcher]'); + + assert.dom('[data-test-side-menu-switcher-modal]').exists(); + } else { + assert.dom('[data-test-side-menu-switcher]').doesNotExist(); + } + } + ); +}); diff --git a/tests/integration/components/appknox-wrapper/onboarding-guide-test.js b/tests/integration/components/appknox-wrapper/onboarding-guide-test.js new file mode 100644 index 000000000..3f3e570df --- /dev/null +++ b/tests/integration/components/appknox-wrapper/onboarding-guide-test.js @@ -0,0 +1,89 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { setupIntl, t } from 'ember-intl/test-support'; +import { findAll, render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +import Service from '@ember/service'; + +class IntegrationStub extends Service { + async configure(user) { + this.currentUser = user; + } + + isCrispEnabled() { + return true; + } +} + +module('Integration | Component | Onboarding Guide', function (hooks) { + setupRenderingTest(hooks); + setupMirage(hooks); + setupIntl(hooks); + + hooks.beforeEach(async function () { + this.owner.register('service:integration', IntegrationStub); + + this.setProperties({ + onClose: () => {}, + }); + }); + + test('it renders onboarding guide', async function (assert) { + await render(hbs` + + `); + + assert.dom('[data-test-onboarding-guide-modal]').exists(); + + assert + .dom('[data-test-onboarding-guide-modal]') + .containsText(t('onboardingGuides')); + + assert.dom('[data-test-onboarding-guide-category]').exists({ count: 3 }); + + const categories = findAll('[data-test-onboarding-guide-category]'); + + assert + .dom(categories[0]) + .containsText(this.intl.t('onboardingGuideModule.VA')); + + assert.dom(categories[1]).containsText(this.intl.t('SBOM')); + assert.dom(categories[2]).containsText(this.intl.t('appMonitoring')); + + assert.dom('[data-test-onboarding-guide-list-item]').exists({ count: 7 }); + + const guides = findAll('[data-test-onboarding-guide-list-item]'); + + assert + .dom(guides[0]) + .containsText(this.intl.t('onboardingGuideModule.initiateVA')); + + assert + .dom(guides[1]) + .containsText(this.intl.t('onboardingGuideModule.viewReports')); + + assert.dom(guides[2]).containsText(this.intl.t('inviteUsers')); + + assert + .dom(guides[3]) + .containsText(this.intl.t('onboardingGuideModule.createTeams')); + + assert + .dom(guides[4]) + .containsText(this.intl.t('onboardingGuideModule.uploadAccess')); + + assert + .dom(guides[5]) + .containsText(this.intl.t('onboardingGuideModule.generateSBOM')); + + assert + .dom(guides[6]) + .containsText(this.intl.t('onboardingGuideModule.detectDrift')); + + assert.dom('[data-test-onboarding-guide-iframe="va-guide"]').exists(); + }); +}); diff --git a/tests/integration/components/home-page/organization-dashboard/header-test.js b/tests/integration/components/home-page/organization-dashboard/header-test.js deleted file mode 100644 index 47d0166d2..000000000 --- a/tests/integration/components/home-page/organization-dashboard/header-test.js +++ /dev/null @@ -1,255 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { setupMirage } from 'ember-cli-mirage/test-support'; -import { setupIntl, t } from 'ember-intl/test-support'; -import { click, findAll, render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -import Service from '@ember/service'; -import ENUMS from 'irene/enums'; - -class NotificationsStub extends Service { - errorMsg = null; - successMsg = null; - - error(msg) { - this.errorMsg = msg; - } - success(msg) { - this.successMsg = msg; - } -} - -class FreshdeskStub extends Service { - supportWidgetIsEnabled = false; - freshchatEnabled = false; -} - -class ConfigurationStub extends Service { - serverData = { urlUploadAllowed: true, enterprise: false }; -} - -class IntegrationStub extends Service { - async configure(user) { - this.currentUser = user; - } - - isCrispEnabled() { - return true; - } -} - -module( - 'Integration | Component | home-page/organization-dashboard/header', - function (hooks) { - setupRenderingTest(hooks); - setupMirage(hooks); - setupIntl(hooks); - - hooks.beforeEach(async function () { - this.server.createList('organization', 1); - - const store = this.owner.lookup('service:store'); - - const organizationMe = store.createRecord('organization-me', { - is_owner: true, - is_admin: true, - }); - - class OrganizationMeStub extends Service { - org = organizationMe; - } - - await this.owner.lookup('service:organization').load(); - - this.owner.register('service:me', OrganizationMeStub); - this.owner.register('service:notifications', NotificationsStub); - this.owner.register('service:freshdesk', FreshdeskStub); - this.owner.register('service:integration', IntegrationStub); - this.owner.register('service:configuration', ConfigurationStub); - - const organization = this.owner.lookup('service:organization'); - - this.setProperties({ - organization: organization, - user: store.createRecord('user', this.server.create('user').toJSON()), - onToggleOnboardingGuide: () => {}, - }); - - // handle submissions api for each test - this.server.get('/submissions', () => []); - }); - - test.each( - 'it renders organization-dashboard header', - [{ knowledgeBase: true, hasUploadAppStatus: true }, {}], - async function (assert, { knowledgeBase, hasUploadAppStatus }) { - if (hasUploadAppStatus) { - this.server.createList('submission', 2, { - status: ENUMS.SUBMISSION_STATUS.VALIDATING, - }); - - this.server.get('/submissions', (schema) => { - return schema.submissions.all().models; - }); - } - - const freshdesk = this.owner.lookup('service:freshdesk'); - freshdesk.supportWidgetIsEnabled = knowledgeBase; - - await render(hbs` - - `); - - assert.dom('[data-test-organizationDashboardHeader]').exists(); - - assert - .dom('[data-test-organizationDashboardHeader-startScanLabel]') - .hasText(t('startNewScan')); - - assert.dom('[data-test-uploadApp-input]').exists(); - - assert.dom('[data-test-uploadApp-uploadBtn]').hasText(t('uploadApp')); - // assert.dom('[data-test-uploadAppViaLink-btn]').isNotDisabled(); - - if (hasUploadAppStatus) { - assert.dom('[data-test-uploadAppStatus-loader]').exists(); - assert.dom('[data-test-uploadAppStatus-icon]').exists(); - } else { - assert.dom('[data-test-uploadAppStatus-loader]').doesNotExist(); - assert.dom('[data-test-uploadAppStatus-icon]').doesNotExist(); - } - - assert - .dom('[data-test-organizationDashboardHeader-OnboardingGuideBtn]') - .exists(); - - if (knowledgeBase) { - assert - .dom('[data-test-organizationDashboardHeader-KnowledgeBaseBtn]') - .isNotDisabled() - .hasText(t('knowledgeBase')); - } else { - assert - .dom('[data-test-organizationDashboardHeader-KnowledgeBaseBtn]') - .doesNotExist(); - } - - assert.dom('[data-test-bell-icon]').isNotDisabled(); - - assert - .dom('[data-test-organizationDashboardHeader-profileBtn]') - .isNotDisabled() - .hasText(this.user.username); - } - ); - - test('It should not show onboarding guide for enterprise', async function (assert) { - const configuration = this.owner.lookup('service:configuration'); - configuration.serverData.enterprise = true; - - await render(hbs` - - `); - - assert - .dom('[data-test-organizationDashboardHeader-OnboardingGuideBtn]') - .doesNotExist(); - }); - - test('test Knowledge Base click', async function (assert) { - assert.expect(5); - - const freshdesk = this.owner.lookup('service:freshdesk'); - - freshdesk.supportWidgetIsEnabled = true; - - freshdesk.openSupportWidget = function () { - assert.ok('Knowledge base clicked'); - assert.strictEqual(arguments.length, 0); - }; - - await render(hbs` - - `); - - assert.dom('[data-test-organizationDashboardHeader]').exists(); - - assert - .dom('[data-test-organizationDashboardHeader-KnowledgeBaseBtn]') - .isNotDisabled() - .hasText(t('knowledgeBase')); - - await click('[data-test-organizationDashboardHeader-KnowledgeBaseBtn]'); - }); - - test.each( - 'test profile btn and logout', - [{ chatSupport: true }, { chatSupport: false }], - async function (assert, { chatSupport }) { - this.set('logoutAction', () => { - assert.ok('Logout action called'); - }); - - await render(hbs` - - `); - - const freshdesk = this.owner.lookup('service:freshdesk'); - freshdesk.freshchatEnabled = chatSupport; - - assert.dom('[data-test-organizationDashboardHeader]').exists(); - - assert - .dom('[data-test-organizationDashboardHeader-profileBtn]') - .isNotDisabled() - .hasText(this.user.username); - - await click('[data-test-organizationDashboardHeader-profileBtn]'); - - assert - .dom('[data-test-organizationDashboardHeader-profileMenuItem]') - .exists(); - - const menuItems = findAll( - '[data-test-organizationDashboardHeader-profileMenuItem]' - ); - - assert.dom(menuItems[0]).hasText(this.user.username); - assert.dom(menuItems[1]).hasText(this.user.email); - - if (chatSupport) { - assert.dom(menuItems[2]).hasText(t('support')); - assert.dom(menuItems[3]).hasText(t('logout')); - - await click(menuItems[3].querySelector('button')); - - assert - .dom('[data-test-organizationDashboardHeader-profileMenuItem]') - .doesNotExist(); - } else { - assert.dom(menuItems[2]).hasText(t('logout')); - - await click(menuItems[2].querySelector('button')); - - assert - .dom('[data-test-organizationDashboardHeader-profileMenuItem]') - .doesNotExist(); - } - } - ); - } -); diff --git a/tests/integration/components/home-page/organization-dashboard/onboarding-guide-test.js b/tests/integration/components/home-page/organization-dashboard/onboarding-guide-test.js deleted file mode 100644 index 9ea9fc054..000000000 --- a/tests/integration/components/home-page/organization-dashboard/onboarding-guide-test.js +++ /dev/null @@ -1,92 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { setupMirage } from 'ember-cli-mirage/test-support'; -import { setupIntl, t } from 'ember-intl/test-support'; -import { findAll, render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -import Service from '@ember/service'; - -class IntegrationStub extends Service { - async configure(user) { - this.currentUser = user; - } - - isCrispEnabled() { - return true; - } -} - -module( - 'Integration | Component | home-page/organization-dashboard/header', - function (hooks) { - setupRenderingTest(hooks); - setupMirage(hooks); - setupIntl(hooks); - - hooks.beforeEach(async function () { - this.owner.register('service:integration', IntegrationStub); - - this.setProperties({ - onClose: () => {}, - }); - }); - - test('it renders onboarding guide', async function (assert) { - await render(hbs` - - `); - - assert.dom('[data-test-onboarding-guide-modal]').exists(); - - assert - .dom('[data-test-onboarding-guide-modal]') - .containsText(t('onboardingGuides')); - - assert.dom('[data-test-onboarding-guide-category]').exists({ count: 3 }); - - const categories = findAll('[data-test-onboarding-guide-category]'); - - assert - .dom(categories[0]) - .containsText(this.intl.t('onboardingGuideModule.VA')); - - assert.dom(categories[1]).containsText(this.intl.t('SBOM')); - assert.dom(categories[2]).containsText(this.intl.t('appMonitoring')); - - assert.dom('[data-test-onboarding-guide-list-item]').exists({ count: 7 }); - - const guides = findAll('[data-test-onboarding-guide-list-item]'); - - assert - .dom(guides[0]) - .containsText(this.intl.t('onboardingGuideModule.initiateVA')); - - assert - .dom(guides[1]) - .containsText(this.intl.t('onboardingGuideModule.viewReports')); - - assert.dom(guides[2]).containsText(this.intl.t('inviteUsers')); - - assert - .dom(guides[3]) - .containsText(this.intl.t('onboardingGuideModule.createTeams')); - - assert - .dom(guides[4]) - .containsText(this.intl.t('onboardingGuideModule.uploadAccess')); - - assert - .dom(guides[5]) - .containsText(this.intl.t('onboardingGuideModule.generateSBOM')); - - assert - .dom(guides[6]) - .containsText(this.intl.t('onboardingGuideModule.detectDrift')); - - assert.dom('[data-test-onboarding-guide-iframe="va-guide"]').exists(); - }); - } -); diff --git a/tests/integration/components/home-page/organization-dashboard/side-nav-test.js b/tests/integration/components/home-page/organization-dashboard/side-nav-test.js deleted file mode 100644 index 83ba079e0..000000000 --- a/tests/integration/components/home-page/organization-dashboard/side-nav-test.js +++ /dev/null @@ -1,377 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, findAll, click, find } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; -import { setupMirage } from 'ember-cli-mirage/test-support'; -import { setupIntl, t } from 'ember-intl/test-support'; -import ENV from 'irene/config/environment'; - -import Service from '@ember/service'; - -class NotificationsStub extends Service { - errorMsg = null; - successMsg = null; - - error(msg) { - this.errorMsg = msg; - } - success(msg) { - this.successMsg = msg; - } -} - -class ConfigurationStub extends Service { - frontendData = {}; - themeData = {}; - imageData = {}; - serverData = { - enterprise: true, - }; -} - -class IntegrationStub extends Service { - async configure(user) { - this.currentUser = user; - } - - isPendoEnabled() { - return false; - } - - isCrispEnabled() { - return true; - } -} - -class FreshdeskStub extends Service { - freshchatEnabled = true; -} - -const menuItems = ({ - analytics, - appMonitoring, - publicApis, - market, - billing, - partner, - security, - sbom, -}) => - [ - { label: t('allProjects'), icon: 'folder', hasBadge: true }, - appMonitoring && { label: t('appMonitoring'), icon: 'inventory-2' }, - sbom && { label: t('SBOM'), icon: 'receipt-long' }, - analytics && { label: t('analytics'), icon: 'graphic-eq' }, - { label: t('organization'), icon: 'people' }, - publicApis && { label: t('apiDocumentation') }, - { label: t('accountSettings'), icon: 'account-box' }, - market && { label: t('marketplace'), icon: 'account-balance' }, - billing && { label: t('billing'), icon: 'credit-card' }, - partner && { - label: t('clients'), - icon: 'groups-2', - hasBadge: true, - badgeLabel: t('beta'), - }, - security && { label: t('securityDashboard'), icon: 'security' }, - ].filter(Boolean); - -const sections = (enabled) => ({ - billing: enabled, - appMonitoring: enabled, - market: enabled, - partner: enabled, - security: enabled, - sbom: enabled, - publicApis: enabled, -}); - -const lowerMenuItems = [ - { - title: () => t('chatSupport'), - icon: 'chat-bubble', - }, - { - title: () => t('version'), - icon: 'info', - enablePendo: false, - divider: true, - }, - { - title: () => t('collapse'), - icon: 'keyboard-tab', - }, -]; - -module( - 'Integration | Component | home-page/organization-dashboard/side-nav', - function (hooks) { - setupRenderingTest(hooks); - setupMirage(hooks); - setupIntl(hooks); - - hooks.beforeEach(async function () { - this.server.createList('organization', 1); - - const store = this.owner.lookup('service:store'); - - const organizationMe = store.createRecord('organization-me', { - is_owner: true, - is_admin: true, - }); - - class OrganizationMeStub extends Service { - org = organizationMe; - } - - await this.owner.lookup('service:organization').load(); - - this.owner.register('service:me', OrganizationMeStub); - this.owner.register('service:integration', IntegrationStub); - this.owner.register('service:notifications', NotificationsStub); - - const organization = this.owner.lookup('service:organization'); - - this.setProperties({ - organization: organization, - }); - }); - - test.each( - 'it should render org-side-menu', - [ - { owner: true, admin: true, ...sections(true) }, - { owner: true, admin: true, ...sections(false) }, - { owner: false, admin: true, ...sections(true) }, - { owner: false, admin: true, ...sections(false) }, - { owner: false, admin: false, ...sections(true) }, - { owner: false, admin: false, ...sections(false) }, - ], - async function ( - assert, - { - owner, - admin, - billing, - partner, - market, - appMonitoring, - security, - sbom, - publicApis, - } - ) { - this.setProperties({ - isSecurityEnabled: security, - isCollapsed: false, - toggleSidebar: () => { - this.isCollapsed = !this.isCollapsed; - }, - }); - - const me = this.owner.lookup('service:me'); - - me.org.is_owner = owner; - me.org.is_admin = admin; - me.org.can_access_partner_dashboard = partner; - - ENV.enableMarketplace = market; - - this.organization.selected.billingHidden = !billing; - - this.organization.selected.features = { - app_monitoring: appMonitoring, - public_apis: publicApis, - sbom, - }; - - await render( - hbs`` - ); - - assert.dom('[data-test-img-logo]').exists(); - - const menuItemEle = findAll('[data-test-side-menu-item]'); - - menuItems({ - analytics: owner || admin, - billing: owner && billing, - market, - appMonitoring, - partner, - security, - sbom, - publicApis, - }).forEach((it, index) => { - if (it.icon) { - assert - .dom('[data-test-side-menu-item-icon]', menuItemEle[index]) - .hasClass(`ak-icon-${it.icon}`); - } - - assert - .dom('[data-test-side-menu-item-text]', menuItemEle[index]) - .hasText(it.label); - - if (it.hasBadge) { - assert - .dom('[data-test-side-menu-item-badge]', menuItemEle[index]) - .exists() - .hasText( - it.badgeLabel || - String(this.organization.selected.projectsCount) - ); - } - }); - } - ); - - test.each( - 'it should show collapsed menu if isCollapsed is false', - [ - { owner: true, admin: true, ...sections(true) }, - { owner: true, admin: true, ...sections(false) }, - { owner: false, admin: true, ...sections(true) }, - { owner: false, admin: true, ...sections(false) }, - { owner: false, admin: false, ...sections(true) }, - { owner: false, admin: false, ...sections(false) }, - ], - async function ( - assert, - { - owner, - admin, - billing, - partner, - market, - appMonitoring, - security, - sbom, - } - ) { - this.setProperties({ - isCollapsed: true, - isSecurityEnabled: security, - toggleSidebar: () => { - this.isCollapsed = !this.isCollapsed; - }, - }); - - const me = this.owner.lookup('service:me'); - - me.org.is_owner = owner; - me.org.is_admin = admin; - me.org.can_access_partner_dashboard = partner; - - ENV.enableMarketplace = market; - - this.organization.selected.billingHidden = !billing; - - this.organization.selected.features = { - app_monitoring: appMonitoring, - sbom, - }; - - await render( - hbs`` - ); - - const menuItemEle = findAll('[data-test-side-menu-item]'); - - menuItems({ - analytics: owner || admin, - billing: owner && billing, - market, - appMonitoring, - partner, - security, - sbom, - }).forEach((it, index) => { - assert - .dom('[data-test-side-menu-item-icon]', menuItemEle[index]) - .hasClass(`ak-icon-${it.icon}`); - }); - } - ); - - test('it should hide sbom link in side menu if org is an enterprise', async function (assert) { - this.owner.register('service:configuration', ConfigurationStub); - - this.setProperties({ - isCollapsed: false, - toggleSidebar: () => { - this.isCollapsed = !this.isCollapsed; - }, - }); - - await render( - hbs`` - ); - - assert.dom(`[data-test-side-menu-item="${t('SBOM')}"]`).doesNotExist(); - }); - - test('it should show lower menu items', async function (assert) { - this.owner.register('service:freshdesk', FreshdeskStub); - - this.setProperties({ - isCollapsed: false, - toggleSidebar: () => { - this.set('isCollapsed', !this.isCollapsed); - }, - }); - - await render( - hbs`` - ); - - assert.dom('[data-test-side-lower-menu]').exists(); - - assert.dom('[data-test-side-lower-menu-divider]').exists(); - - const lowerMenuItemEle = findAll('[data-test-side-lower-menu-item]'); - - const collapseButton = find(`[ - data-test-side-lower-menu-item="${t('collapse')}" - ]`); - - assert.ok(collapseButton, 'Collapse button should exist'); - - await click(collapseButton); - - assert.ok( - this.isCollapsed, - 'Sidebar should be collapsed after clicking the expand button' - ); - - assert.dom('[data-test-side-lower-menu-item-text]').doesNotExist(); - - lowerMenuItems.forEach((it, index) => { - assert - .dom('[data-test-side-lower-menu-item-icon]', lowerMenuItemEle[index]) - .hasClass(`ak-icon-${it.icon}`); - }); - - const expandButton = find(`[ - data-test-side-lower-menu-item="${t('expand')}" - ]`); - - await click(expandButton); - - assert.notOk( - this.isCollapsed, - 'Sidebar should be expanded after clicking the expand button' - ); - - lowerMenuItems.forEach((it, index) => { - assert - .dom('[data-test-side-lower-menu-item-text]', lowerMenuItemEle[index]) - .containsText(it.title()); - - assert - .dom('[data-test-side-lower-menu-item-icon]', lowerMenuItemEle[index]) - .hasClass(`ak-icon-${it.icon}`); - }); - }); - } -); diff --git a/tests/integration/components/home-page/security-dashboard-nav-test.js b/tests/integration/components/home-page/security-dashboard-nav-test.js deleted file mode 100644 index bd7a8df22..000000000 --- a/tests/integration/components/home-page/security-dashboard-nav-test.js +++ /dev/null @@ -1,84 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { setupMirage } from 'ember-cli-mirage/test-support'; -import { setupIntl } from 'ember-intl/test-support'; -import { click, findAll, render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -import Service from '@ember/service'; - -class IntegrationStub extends Service { - async configure(user) { - this.currentUser = user; - } - - isCrispEnabled() { - return false; - } -} - -module( - 'Integration | Component | home-page/security-dashboard-nav', - function (hooks) { - setupRenderingTest(hooks); - setupMirage(hooks); - setupIntl(hooks); - - hooks.beforeEach(async function () { - const store = this.owner.lookup('service:store'); - - this.owner.register('service:integration', IntegrationStub); - - this.setProperties({ - user: store.createRecord('user', this.server.create('user').toJSON()), - }); - }); - - test('it exists', async function (assert) { - await render(hbs` - - `); - - assert.dom('[data-test-security-appbar]').exists(); - - assert.dom('[data-test-security-appknox-logo]').exists(); - - assert.dom('[data-test-security-profile-btn]').exists(); - }); - - test('test profile btn and logout', async function (assert) { - assert.expect(9); - - this.set('logoutAction', () => { - assert.ok('Logout action called'); - }); - - await render(hbs` - - `); - - assert.dom('[data-test-security-appbar]').exists(); - - assert - .dom('[data-test-security-profile-btn]') - .isNotDisabled() - .hasText(this.user.username); - - await click('[data-test-security-profile-btn]'); - - assert.dom('[data-test-security-profile-menu-item]').exists(); - - const menuItems = findAll('[data-test-security-profile-menu-item]'); - - assert.dom(menuItems[0]).hasText(this.user.username); - assert.dom(menuItems[1]).hasText(this.user.email); - assert.dom(menuItems[2]).hasText('Logout'); - - await click(menuItems[2].querySelector('button')); - - await click('[data-test-security-profile-btn]'); - - assert.dom('[data-test-security-profile-menu-item]').doesNotExist(); - }); - } -); diff --git a/tests/integration/components/security-wrapper-test.js b/tests/integration/components/security-wrapper-test.js new file mode 100644 index 000000000..64a5502b0 --- /dev/null +++ b/tests/integration/components/security-wrapper-test.js @@ -0,0 +1,106 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { setupIntl } from 'ember-intl/test-support'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +import Service from '@ember/service'; + +class NotificationsStub extends Service { + errorMsg = null; + successMsg = null; + + error(msg) { + this.errorMsg = msg; + } + success(msg) { + this.successMsg = msg; + } +} + +class FreshdeskStub extends Service { + supportWidgetIsEnabled = false; + freshchatEnabled = false; +} + +class ConfigurationStub extends Service { + frontendData = {}; + themeData = {}; + imageData = {}; + serverData = { urlUploadAllowed: true, enterprise: false }; +} + +class IntegrationStub extends Service { + async configure(user) { + this.currentUser = user; + } + + isCrispEnabled() { + return true; + } +} + +module('Integration | Component | security-wrapper', function (hooks) { + setupRenderingTest(hooks); + setupMirage(hooks); + setupIntl(hooks); + + hooks.beforeEach(async function () { + this.server.createList('organization', 1); + + const store = this.owner.lookup('service:store'); + + const organizationMe = store.createRecord('organization-me', { + is_owner: true, + is_admin: true, + }); + + class OrganizationMeStub extends Service { + org = organizationMe; + } + + await this.owner.lookup('service:organization').load(); + + this.owner.register('service:me', OrganizationMeStub); + this.owner.register('service:notifications', NotificationsStub); + this.owner.register('service:freshdesk', FreshdeskStub); + this.owner.register('service:integration', IntegrationStub); + this.owner.register('service:configuration', ConfigurationStub); + + const organization = this.owner.lookup('service:organization'); + + this.setProperties({ + organization: organization, + user: store.createRecord('user', this.server.create('user').toJSON()), + }); + + // handle submissions api for each test + this.server.get('/submissions', () => []); + }); + + test('it renders main top-nav with logo', async function (assert) { + await render(hbs` + + `); + + assert.dom('[data-test-topNav]').exists(); + + assert.dom('[data-test-topNav-logo]').exists(); + + assert.dom('[data-test-topNav-title]').doesNotExist(); + + assert.dom('[data-test-security-nav-menu]').exists(); + + assert.dom('[data-test-side-nav]').doesNotExist(); + + assert.dom('[data-test-bell-icon]').doesNotExist(); + + assert + .dom('[data-test-topNav-profileBtn]') + .isNotDisabled() + .hasText(this.user.username); + }); +}); diff --git a/tests/integration/components/storeknox-wrapper-test.js b/tests/integration/components/storeknox-wrapper-test.js new file mode 100644 index 000000000..75f0304d5 --- /dev/null +++ b/tests/integration/components/storeknox-wrapper-test.js @@ -0,0 +1,166 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { setupIntl, t } from 'ember-intl/test-support'; +import { click, find, findAll, render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +import Service from '@ember/service'; + +class NotificationsStub extends Service { + errorMsg = null; + successMsg = null; + + error(msg) { + this.errorMsg = msg; + } + success(msg) { + this.successMsg = msg; + } +} + +class FreshdeskStub extends Service { + supportWidgetIsEnabled = false; + freshchatEnabled = false; +} + +class ConfigurationStub extends Service { + frontendData = {}; + themeData = {}; + imageData = {}; + serverData = { urlUploadAllowed: true, enterprise: false }; +} + +class IntegrationStub extends Service { + async configure(user) { + this.currentUser = user; + } + + isCrispEnabled() { + return true; + } +} + +const storeknoxMenuItems = [ + { + label: () => t('discovery'), + icon: 'search', + }, + { + label: () => t('inventory'), + icon: 'inventory-2', + }, +].filter(Boolean); + +module('Integration | Component | storeknox-wrapper', function (hooks) { + setupRenderingTest(hooks); + setupMirage(hooks); + setupIntl(hooks); + + hooks.beforeEach(async function () { + this.server.createList('organization', 1); + + const store = this.owner.lookup('service:store'); + + const organizationMe = store.createRecord('organization-me', { + is_owner: true, + is_admin: true, + }); + + class OrganizationMeStub extends Service { + org = organizationMe; + } + + await this.owner.lookup('service:organization').load(); + + this.owner.register('service:me', OrganizationMeStub); + this.owner.register('service:notifications', NotificationsStub); + this.owner.register('service:freshdesk', FreshdeskStub); + this.owner.register('service:integration', IntegrationStub); + this.owner.register('service:configuration', ConfigurationStub); + + const organization = this.owner.lookup('service:organization'); + + this.setProperties({ + title: t('storeknox'), + organization: organization, + user: store.createRecord('user', this.server.create('user').toJSON()), + }); + + // handle submissions api for each test + this.server.get('/submissions', () => []); + }); + + test('it renders main top-nav with title', async function (assert) { + await render(hbs` + + `); + + assert.dom('[data-test-topNav]').exists(); + + assert.dom('[data-test-topNav-title]').hasText(this.title); + + assert.dom('[data-test-bell-icon]').isNotDisabled(); + + assert + .dom('[data-test-topNav-profileBtn]') + .isNotDisabled() + .hasText(this.user.username); + }); + + test('it should render storeknox side nav', async function (assert) { + await render(hbs` + + `); + + const menuItemEle = findAll('[data-test-side-menu-item]'); + + storeknoxMenuItems.forEach((it, index) => { + assert + .dom('[data-test-side-menu-item-icon]', menuItemEle[index]) + .hasClass(`ak-icon-${it.icon}`); + }); + + assert.dom('[data-test-side-lower-menu]').exists(); + + assert.dom('[data-test-side-lower-menu-divider]').exists(); + + const expandButton = find( + `[data-test-side-lower-menu-item="${t('expand')}"]` + ); + + await click(expandButton); + + storeknoxMenuItems.forEach((it, index) => { + assert + .dom('[data-test-side-menu-item-text]', menuItemEle[index]) + .hasText(it.label()); + }); + + assert + .dom('[data-test-side-lower-menu-item-text]') + .containsText(t('collapse')); + + assert + .dom('[data-test-side-lower-menu-item-icon]') + .hasClass('ak-icon-keyboard-tab'); + + const collapseButton = find( + `[data-test-side-lower-menu-item="${t('collapse')}"]` + ); + + assert.ok(collapseButton, 'Collapse button should exist'); + + await click(collapseButton); + + assert.dom('[data-test-side-lower-menu-item-text]').doesNotExist(); + + assert + .dom('[data-test-side-lower-menu-item-icon]') + .hasClass('ak-icon-keyboard-tab'); + }); +}); diff --git a/tests/integration/components/top-nav-test.js b/tests/integration/components/top-nav-test.js new file mode 100644 index 000000000..00424f8d0 --- /dev/null +++ b/tests/integration/components/top-nav-test.js @@ -0,0 +1,161 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { setupMirage } from 'ember-cli-mirage/test-support'; +import { setupIntl, t } from 'ember-intl/test-support'; +import { click, findAll, render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +import Service from '@ember/service'; + +class NotificationsStub extends Service { + errorMsg = null; + successMsg = null; + + error(msg) { + this.errorMsg = msg; + } + success(msg) { + this.successMsg = msg; + } +} + +class FreshdeskStub extends Service { + supportWidgetIsEnabled = false; + freshchatEnabled = false; +} + +class ConfigurationStub extends Service { + frontendData = {}; + themeData = {}; + imageData = {}; + serverData = { urlUploadAllowed: true, enterprise: false }; +} + +class IntegrationStub extends Service { + async configure(user) { + this.currentUser = user; + } + + isCrispEnabled() { + return true; + } +} + +module('Integration | Component | top-nav', function (hooks) { + setupRenderingTest(hooks); + setupMirage(hooks); + setupIntl(hooks); + + hooks.beforeEach(async function () { + this.server.createList('organization', 1); + + const store = this.owner.lookup('service:store'); + + const organizationMe = store.createRecord('organization-me', { + is_owner: true, + is_admin: true, + }); + + class OrganizationMeStub extends Service { + org = organizationMe; + } + + await this.owner.lookup('service:organization').load(); + + this.owner.register('service:me', OrganizationMeStub); + this.owner.register('service:notifications', NotificationsStub); + this.owner.register('service:freshdesk', FreshdeskStub); + this.owner.register('service:integration', IntegrationStub); + this.owner.register('service:configuration', ConfigurationStub); + + const organization = this.owner.lookup('service:organization'); + + this.setProperties({ + title: t('vapt'), + organization: organization, + user: store.createRecord('user', this.server.create('user').toJSON()), + }); + + // handle submissions api for each test + this.server.get('/submissions', () => []); + }); + + test('it renders main top-nav with title', async function (assert) { + await render(hbs` + + `); + + assert.dom('[data-test-topNav]').exists(); + + assert.dom('[data-test-topNav-title]').hasText(this.title); + + assert.dom('[data-test-bell-icon]').isNotDisabled(); + + assert + .dom('[data-test-topNav-profileBtn]') + .isNotDisabled() + .hasText(this.user.username); + }); + + test('it renders main top-nav without title', async function (assert) { + await render(hbs` + + `); + + assert.dom('[data-test-topNav]').exists(); + + assert.dom('[data-test-topNav-logo]').exists(); + + assert.dom('[data-test-bell-icon]').isNotDisabled(); + + assert + .dom('[data-test-topNav-profileBtn]') + .isNotDisabled() + .hasText(this.user.username); + }); + + test.each( + 'test profile btn menu items', + [{ chatSupport: true }, { chatSupport: false }], + async function (assert, { chatSupport }) { + await render(hbs` + + `); + + const freshdesk = this.owner.lookup('service:freshdesk'); + freshdesk.freshchatEnabled = chatSupport; + freshdesk.logUserOutOfSupportWidget = () => {}; + + assert.dom('[data-test-topNav]').exists(); + + assert + .dom('[data-test-topNav-profileBtn]') + .isNotDisabled() + .hasText(this.user.username); + + await click('[data-test-topNav-profileBtn]'); + + assert.dom('[data-test-topNav-profileMenuItem]').exists(); + + const menuItems = findAll('[data-test-topNav-profileMenuItem]'); + + assert.dom(menuItems[0]).hasText(this.user.username); + assert.dom(menuItems[1]).hasText(this.user.email); + + if (chatSupport) { + assert.dom(menuItems[2]).hasText(t('support')); + assert.dom(menuItems[3]).hasText(t('logout')); + } else { + assert.dom(menuItems[2]).hasText(t('logout')); + } + } + ); +}); diff --git a/translations/en.json b/translations/en.json index 21876a16b..20a0da88e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -77,7 +77,10 @@ "apiURLFilter": "API URL Filter", "app": "app", "appCenterPipeline": "App Center Build", + "appknox": "Appknox", + "appknoxDesc": "Assess your mobile applications for vulnerabilities", "appOrS": "app(s)", + "appSwitcher": "App Switcher", "appiumFileUploadedSuccessfully": "File Uploaded Successfully", "appiumScheduledAutomation": "Schedule Automation", "appiumScheduledAutomationSuccessOff": "Scheduled dynamic scan automation turned OFF", @@ -360,6 +363,7 @@ "disableThisIn": "You can disable this in", "disabled": "Disabled", "disconnect": "Disconnect", + "discovery": "Discovery", "docs": "Docs", "domainDeleted": "Domain Deleted", "dontHaveAccount": "Don't have an account?", @@ -586,6 +590,7 @@ "high": "High", "hipaa": "HIPAA", "hipaaExpansion": "Health Insurance Portability and Accountability Act", + "home": "Home", "host": "Host", "howAndWhat": "How and What?", "howToFix": "How to fix ?", @@ -640,6 +645,7 @@ "invalidProject": "Please select valid project", "invalidRisk": "Please select valid risk value", "invalidURL": " is an invalid URL", + "inventory": "Inventory", "invitationDeleted": "Invitation is deleted successfully", "invitationReSent": "Invitation re-sent successfully", "invitationRegisterConfirmation": "Thank you for registering with us.", @@ -1073,6 +1079,7 @@ "page": "Page", "pageNotFound": "The page cannot be found", "paid": "Paid", + "partner": "Partner", "partnerPrivilege": { "noListFiles": "You were not privileged to access files", "noListProjects": "You were not privileged to access projects" @@ -1373,6 +1380,7 @@ "security": "Security", "securityDashboard": "Security Dashboard", "selectAnyTeam": "Please select any team", + "selectThePath": "Select the path", "selectDevice": "Select the device", "selectDevicePref": "Select the device preference", "selectLanguage": "Select the preferred language below", @@ -1494,6 +1502,8 @@ "stop": "Stop", "storage": "Storage", "storageManagement": "Storage Management", + "storeknox": "Storeknox", + "storeknoxDesc": "Monitor your mobile apps on Play Store and App Store", "storeLowercase": "store", "submit": "Submit", "submitUrl": "Submit URL", @@ -1508,6 +1518,7 @@ "successfullyReset": "You have successfully reverted back to orginal risk of this analysis", "summary": "Summary", "support": "Support", + "switchTo": "Switch to", "sync": "Sync More", "system": "System", "systemStatus": "System Status", @@ -1515,6 +1526,7 @@ "tags": "Tags", "tablet": "Tablet", "takeMeTo": "Take me to", + "takeMeToDashboard": "Take me to the dashboard", "team": "Team", "teamCreated": "Team Created Successfully", "teamDetails": "Team Details", @@ -1547,6 +1559,7 @@ "toDate": "To Date", "toggleAutomatedDAST": "Turn on the automated DAST", "tokenCopied": "Token Copied!", + "toSecureYourMobileApps": "To Secure Your Mobile Applications", "totalProjects": "Total Projects", "totalUsers": "Total Users", "transferCredits": "Transfer Credits", @@ -1609,6 +1622,7 @@ "usernameEmailIdTextPlaceholder": "Enter your Username/Email ID", "validUntil": "Valid until", "validity": "Validity", + "vapt": "VAPT", "verify": "Verify", "version": "Version", "versions": "Versions", diff --git a/translations/ja.json b/translations/ja.json index 7f1eaa4d8..49652d188 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -77,7 +77,10 @@ "apiURLFilter": "API URLフィルタ", "app": "アプリ", "appCenterPipeline": "App Center Build", + "appknox": "Appknox", + "appknoxDesc": "Assess your mobile applications for vulnerabilities", "appOrS": "アプリ", + "appSwitcher": "App Switcher", "appiumFileUploadedSuccessfully": "File Uploaded Successfully", "appiumScheduledAutomation": "Schedule Automation", "appiumScheduledAutomationSuccessOff": "Scheduled dynamic scan automation turned OFF", @@ -360,6 +363,7 @@ "disableThisIn": "You can disable this in", "disabled": "無効", "disconnect": "Disconnect", + "discovery": "Discovery", "docs": "Docs", "domainDeleted": "Domain Deleted", "dontHaveAccount": "Don't have an account?", @@ -586,6 +590,7 @@ "high": "高", "hipaa": "HIPAA", "hipaaExpansion": "Health Insurance Portability and Accountability Act", + "home": "Home", "host": "Host", "howAndWhat": "どのように?", "howToFix": "How to fix ?", @@ -640,6 +645,7 @@ "invalidProject": "Please select valid project", "invalidRisk": "Please select valid risk value", "invalidURL": "は無効なURLです", + "inventory": "Inventory", "invitationDeleted": "Invitation is deleted successfully", "invitationReSent": "Invitation re-sent successfully", "invitationRegisterConfirmation": "Thank you for registering with us.", @@ -1073,6 +1079,7 @@ "page": "Page", "pageNotFound": "ページが見つかりません", "paid": "有料", + "partner": "Partner", "partnerPrivilege": { "noListFiles": "You were not privileged to access files", "noListProjects": "You were not privileged to access projects" @@ -1373,6 +1380,7 @@ "security": "セキュリティ", "securityDashboard": "Security Dashboard", "selectAnyTeam": "チームを選択してください", + "selectThePath": "Select the path", "selectDevice": "デバイスを選択する", "selectDevicePref": "Select the device preference", "selectLanguage": "以下より言語を選択してください", @@ -1494,6 +1502,8 @@ "stop": "停止中", "storage": "Storage", "storageManagement": "Storage Management", + "storeknox": "Storeknox", + "storeknoxDesc": "Monitor your mobile apps on Play Store and App Store", "storeLowercase": "store", "submit": "Submit", "submitUrl": "URLを送信", @@ -1508,6 +1518,7 @@ "successfullyReset": "You have successfully reverted back to orginal risk of this analysis", "summary": "サマリー", "support": "サポート", + "switchTo": "Switch to", "sync": "同期する", "system": "System", "systemStatus": "System Status", @@ -1515,6 +1526,7 @@ "tags": "Tags", "tablet": "タブレット", "takeMeTo": "Take me to", + "takeMeToDashboard": "Take me to the dashboard", "team": "チーム", "teamCreated": "チームが作成されました", "teamDeleted": "削除されました", @@ -1547,6 +1559,7 @@ "toDate": "To Date", "toggleAutomatedDAST": "Turn on the automated DAST", "tokenCopied": "トークンがコピーされました", + "toSecureYourMobileApps": "To Secure Your Mobile Applications", "totalProjects": "Total Projects", "totalUsers": "Total Users", "transferCredits": "Transfer Credits", @@ -1609,6 +1622,7 @@ "usernameEmailIdTextPlaceholder": "Enter your Username/Email ID", "validUntil": "Valid until", "validity": "Validity", + "vapt": "VAPT", "verify": "Verify", "version": "バージョン", "versions": "Versions", diff --git a/types/ak-svg.d.ts b/types/ak-svg.d.ts index a3f016f6a..c9e5bfb41 100644 --- a/types/ak-svg.d.ts +++ b/types/ak-svg.d.ts @@ -42,6 +42,11 @@ export enum AkSvgComponentInvocationByNames { DastAutomationUpselling, NoApiUrlFilter, ToggleAutomatedDast, + AppknoxBgImg, + StoreknoxBgImg, + ConfigurationBgImg, + VpIndicator, + SmIndicator, } export enum AkSvgComponentInvocationByPaths {