You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GlobalNavMenu.tsx 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import classNames from 'classnames';
  21. import * as React from 'react';
  22. import { Link } from 'react-router';
  23. import { isMySet } from '../../../../apps/issues/utils';
  24. import Dropdown from '../../../../components/controls/Dropdown';
  25. import DropdownIcon from '../../../../components/icons/DropdownIcon';
  26. import { translate } from '../../../../helpers/l10n';
  27. import { getQualityGatesUrl } from '../../../../helpers/urls';
  28. import { AppState } from '../../../../types/appstate';
  29. import { ComponentQualifier } from '../../../../types/component';
  30. import { CurrentUser, Extension } from '../../../../types/types';
  31. import withAppStateContext from '../../app-state/withAppStateContext';
  32. interface Props {
  33. appState: AppState;
  34. currentUser: CurrentUser;
  35. location: { pathname: string };
  36. }
  37. export class GlobalNavMenu extends React.PureComponent<Props> {
  38. renderProjects() {
  39. const active =
  40. this.props.location.pathname.startsWith('/projects') &&
  41. this.props.location.pathname !== '/projects/create';
  42. return (
  43. <li>
  44. <Link className={classNames({ active })} to="/projects">
  45. {translate('projects.page')}
  46. </Link>
  47. </li>
  48. );
  49. }
  50. renderPortfolios() {
  51. return (
  52. <li>
  53. <Link activeClassName="active" to="/portfolios">
  54. {translate('portfolios.page')}
  55. </Link>
  56. </li>
  57. );
  58. }
  59. renderIssuesLink() {
  60. const active = this.props.location.pathname.startsWith('/issues');
  61. const query =
  62. this.props.currentUser.isLoggedIn && isMySet()
  63. ? { resolved: 'false', myIssues: 'true' }
  64. : { resolved: 'false' };
  65. return (
  66. <li>
  67. <Link className={classNames({ active })} to={{ pathname: '/issues', query }}>
  68. {translate('issues.page')}
  69. </Link>
  70. </li>
  71. );
  72. }
  73. renderRulesLink() {
  74. return (
  75. <li>
  76. <Link activeClassName="active" to="/coding_rules">
  77. {translate('coding_rules.page')}
  78. </Link>
  79. </li>
  80. );
  81. }
  82. renderProfilesLink() {
  83. return (
  84. <li>
  85. <Link activeClassName="active" to="/profiles">
  86. {translate('quality_profiles.page')}
  87. </Link>
  88. </li>
  89. );
  90. }
  91. renderQualityGatesLink() {
  92. return (
  93. <li>
  94. <Link activeClassName="active" to={getQualityGatesUrl()}>
  95. {translate('quality_gates.page')}
  96. </Link>
  97. </li>
  98. );
  99. }
  100. renderAdministrationLink() {
  101. if (!this.props.appState.canAdmin) {
  102. return null;
  103. }
  104. return (
  105. <li>
  106. <Link activeClassName="active" to="/admin">
  107. {translate('layout.settings')}
  108. </Link>
  109. </li>
  110. );
  111. }
  112. renderGlobalPageLink = ({ key, name }: Extension) => {
  113. return (
  114. <li key={key}>
  115. <Link to={`/extension/${key}`}>{name}</Link>
  116. </li>
  117. );
  118. };
  119. renderMore() {
  120. const { globalPages = [] } = this.props.appState;
  121. const withoutPortfolios = globalPages.filter(page => page.key !== 'governance/portfolios');
  122. if (withoutPortfolios.length === 0) {
  123. return null;
  124. }
  125. return (
  126. <Dropdown
  127. overlay={<ul className="menu">{withoutPortfolios.map(this.renderGlobalPageLink)}</ul>}
  128. tagName="li">
  129. {({ onToggleClick, open }) => (
  130. <a
  131. aria-expanded={open}
  132. aria-haspopup="menu"
  133. role="button"
  134. className={classNames('dropdown-toggle', { active: open })}
  135. href="#"
  136. id="global-navigation-more"
  137. onClick={onToggleClick}>
  138. {translate('more')}
  139. <DropdownIcon className="little-spacer-left text-middle" />
  140. </a>
  141. )}
  142. </Dropdown>
  143. );
  144. }
  145. render() {
  146. const governanceInstalled = this.props.appState.qualifiers.includes(
  147. ComponentQualifier.Portfolio
  148. );
  149. return (
  150. <ul className="global-navbar-menu">
  151. {this.renderProjects()}
  152. {governanceInstalled && this.renderPortfolios()}
  153. {this.renderIssuesLink()}
  154. {this.renderRulesLink()}
  155. {this.renderProfilesLink()}
  156. {this.renderQualityGatesLink()}
  157. {this.renderAdministrationLink()}
  158. {this.renderMore()}
  159. </ul>
  160. );
  161. }
  162. }
  163. export default withAppStateContext(GlobalNavMenu);