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.

AzureProjectCreateRenderer.tsx 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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 { Link, Spinner } from '@sonarsource/echoes-react';
  21. import {
  22. FlagMessage,
  23. InputSearch,
  24. LightPrimary,
  25. PageContentFontWrapper,
  26. Title,
  27. } from 'design-system';
  28. import * as React from 'react';
  29. import { FormattedMessage } from 'react-intl';
  30. import { queryToSearchString } from '~sonar-aligned/helpers/urls';
  31. import { useAppState } from '../../../../app/components/app-state/withAppStateContext';
  32. import { AvailableFeaturesContext } from '../../../../app/components/available-features/AvailableFeaturesContext';
  33. import { translate } from '../../../../helpers/l10n';
  34. import { getGlobalSettingsUrl } from '../../../../helpers/urls';
  35. import { AzureProject, AzureRepository } from '../../../../types/alm-integration';
  36. import { AlmKeys, AlmSettingsInstance } from '../../../../types/alm-settings';
  37. import { Feature } from '../../../../types/features';
  38. import { Dict } from '../../../../types/types';
  39. import { ALM_INTEGRATION_CATEGORY } from '../../../settings/constants';
  40. import AlmSettingsInstanceDropdown from '../components/AlmSettingsInstanceDropdown';
  41. import WrongBindingCountAlert from '../components/WrongBindingCountAlert';
  42. import { CreateProjectModes } from '../types';
  43. import AzurePersonalAccessTokenForm from './AzurePersonalAccessTokenForm';
  44. import AzureProjectsList from './AzureProjectsList';
  45. export interface AzureProjectCreateRendererProps {
  46. loading: boolean;
  47. loadingRepositories: Dict<boolean>;
  48. onImportRepository: (resository: AzureRepository) => void;
  49. onOpenProject: (key: string) => void;
  50. onPersonalAccessTokenCreate: () => void;
  51. onSearch: (query: string) => void;
  52. projects?: AzureProject[];
  53. repositories: Dict<AzureRepository[]>;
  54. searching?: boolean;
  55. searchResults?: AzureRepository[];
  56. searchQuery?: string;
  57. almInstances?: AlmSettingsInstance[];
  58. selectedAlmInstance?: AlmSettingsInstance;
  59. showPersonalAccessTokenForm?: boolean;
  60. resetPat: boolean;
  61. onSelectedAlmInstanceChange: (instance: AlmSettingsInstance) => void;
  62. }
  63. export default function AzureProjectCreateRenderer(
  64. props: Readonly<AzureProjectCreateRendererProps>,
  65. ) {
  66. const {
  67. loading,
  68. loadingRepositories,
  69. projects,
  70. repositories,
  71. searching,
  72. searchResults,
  73. searchQuery,
  74. almInstances,
  75. showPersonalAccessTokenForm,
  76. resetPat,
  77. selectedAlmInstance,
  78. } = props;
  79. const isMonorepoSupported = React.useContext(AvailableFeaturesContext).includes(
  80. Feature.MonoRepositoryPullRequestDecoration,
  81. );
  82. const { canAdmin } = useAppState();
  83. const showCountError = !loading && (!almInstances || almInstances.length === 0);
  84. const showUrlError =
  85. !loading && selectedAlmInstance !== undefined && selectedAlmInstance.url === undefined;
  86. return (
  87. <PageContentFontWrapper>
  88. <header className="sw-mb-10">
  89. <Title className="sw-mb-4">{translate('onboarding.create_project.azure.title')}</Title>
  90. <LightPrimary className="sw-body-sm">
  91. {isMonorepoSupported ? (
  92. <FormattedMessage
  93. id="onboarding.create_project.azure.subtitle.with_monorepo"
  94. values={{
  95. monorepoSetupLink: (
  96. <Link
  97. to={{
  98. pathname: '/projects/create',
  99. search: queryToSearchString({
  100. mode: CreateProjectModes.AzureDevOps,
  101. mono: true,
  102. }),
  103. }}
  104. >
  105. <FormattedMessage id="onboarding.create_project.subtitle_monorepo_setup_link" />
  106. </Link>
  107. ),
  108. }}
  109. />
  110. ) : (
  111. <FormattedMessage id="onboarding.create_project.azure.subtitle" />
  112. )}
  113. </LightPrimary>
  114. </header>
  115. <AlmSettingsInstanceDropdown
  116. almKey={AlmKeys.Azure}
  117. almInstances={almInstances}
  118. selectedAlmInstance={selectedAlmInstance}
  119. onChangeConfig={props.onSelectedAlmInstanceChange}
  120. />
  121. <Spinner isLoading={loading} />
  122. {showUrlError && (
  123. <FlagMessage variant="error" className="sw-mb-2">
  124. <span>
  125. {canAdmin ? (
  126. <FormattedMessage
  127. defaultMessage={translate('onboarding.create_project.azure.no_url.admin')}
  128. id="onboarding.create_project.azure.no_url.admin"
  129. values={{
  130. alm: translate('onboarding.alm', AlmKeys.Azure),
  131. url: (
  132. <Link to={getGlobalSettingsUrl(ALM_INTEGRATION_CATEGORY)}>
  133. {translate('settings.page')}
  134. </Link>
  135. ),
  136. }}
  137. />
  138. ) : (
  139. translate('onboarding.create_project.azure.no_url')
  140. )}
  141. </span>
  142. </FlagMessage>
  143. )}
  144. {showCountError && <WrongBindingCountAlert alm={AlmKeys.Azure} />}
  145. {!loading &&
  146. selectedAlmInstance?.url &&
  147. (showPersonalAccessTokenForm ? (
  148. <div>
  149. <AzurePersonalAccessTokenForm
  150. almSetting={selectedAlmInstance}
  151. onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate}
  152. resetPat={resetPat}
  153. />
  154. </div>
  155. ) : (
  156. <>
  157. <div className="sw-mb-10 sw-w-abs-400">
  158. <InputSearch
  159. onChange={props.onSearch}
  160. placeholder={translate('onboarding.create_project.search_projects_repositories')}
  161. size="full"
  162. />
  163. </div>
  164. <Spinner isLoading={Boolean(searching)}>
  165. <AzureProjectsList
  166. loadingRepositories={loadingRepositories}
  167. onOpenProject={props.onOpenProject}
  168. onImportRepository={props.onImportRepository}
  169. projects={projects}
  170. repositories={repositories}
  171. searchResults={searchResults}
  172. searchQuery={searchQuery}
  173. />
  174. </Spinner>
  175. </>
  176. ))}
  177. </PageContentFontWrapper>
  178. );
  179. }