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.

TutorialSelectionRenderer.tsx 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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 {
  21. Breadcrumbs,
  22. FlagMessage,
  23. GreyCard,
  24. HoverLink,
  25. LightLabel,
  26. LightPrimary,
  27. StandoutLink,
  28. SubTitle,
  29. Title,
  30. } from 'design-system';
  31. import * as React from 'react';
  32. import { AnalysisStatus } from '../../apps/overview/components/AnalysisStatus';
  33. import { isMainBranch } from '../../helpers/branch-like';
  34. import { translate } from '../../helpers/l10n';
  35. import { getBaseUrl } from '../../helpers/system';
  36. import { getProjectTutorialLocation } from '../../helpers/urls';
  37. import { useBranchesQuery } from '../../queries/branch';
  38. import { AlmKeys, AlmSettingsInstance, ProjectAlmBindingResponse } from '../../types/alm-settings';
  39. import { MainBranch } from '../../types/branch-like';
  40. import { Component } from '../../types/types';
  41. import { LoggedInUser } from '../../types/users';
  42. import AzurePipelinesTutorial from './azure-pipelines/AzurePipelinesTutorial';
  43. import BitbucketPipelinesTutorial from './bitbucket-pipelines/BitbucketPipelinesTutorial';
  44. import GitHubActionTutorial from './github-action/GitHubActionTutorial';
  45. import GitLabCITutorial from './gitlabci/GitLabCITutorial';
  46. import JenkinsTutorial from './jenkins/JenkinsTutorial';
  47. import OtherTutorial from './other/OtherTutorial';
  48. import { TutorialModes } from './types';
  49. const DEFAULT_MAIN_BRANCH_NAME = 'main';
  50. export interface TutorialSelectionRendererProps {
  51. almBinding?: AlmSettingsInstance;
  52. baseUrl: string;
  53. component: Component;
  54. currentUser: LoggedInUser;
  55. currentUserCanScanProject: boolean;
  56. loading: boolean;
  57. projectBinding?: ProjectAlmBindingResponse;
  58. selectedTutorial?: TutorialModes;
  59. willRefreshAutomatically?: boolean;
  60. }
  61. function renderAlm(mode: TutorialModes, project: string, icon?: React.ReactNode) {
  62. return (
  63. <GreyCard className="sw-col-span-4 sw-p-4">
  64. <StandoutLink icon={icon} to={getProjectTutorialLocation(project, mode)}>
  65. {translate('onboarding.tutorial.choose_method', mode)}
  66. </StandoutLink>
  67. {mode === TutorialModes.Local && (
  68. <LightLabel as="p" className="sw-mt-3">
  69. {translate('onboarding.mode.help.manual')}
  70. </LightLabel>
  71. )}
  72. {mode === TutorialModes.OtherCI && (
  73. <LightLabel as="p" className="sw-mt-3">
  74. {translate('onboarding.mode.help.otherci')}
  75. </LightLabel>
  76. )}
  77. </GreyCard>
  78. );
  79. }
  80. export default function TutorialSelectionRenderer(props: TutorialSelectionRendererProps) {
  81. const {
  82. almBinding,
  83. baseUrl,
  84. component,
  85. currentUser,
  86. currentUserCanScanProject,
  87. loading,
  88. projectBinding,
  89. selectedTutorial,
  90. willRefreshAutomatically,
  91. } = props;
  92. const { data: { branchLikes } = { branchLikes: [] } } = useBranchesQuery(component);
  93. const mainBranchName =
  94. (branchLikes.find((b) => isMainBranch(b)) as MainBranch | undefined)?.name ||
  95. DEFAULT_MAIN_BRANCH_NAME;
  96. if (loading) {
  97. return <i aria-label={translate('loading')} className="spinner" />;
  98. }
  99. if (!currentUserCanScanProject) {
  100. return (
  101. <FlagMessage className="sw-w-full" variant="warning">
  102. {translate('onboarding.tutorial.no_scan_rights')}
  103. </FlagMessage>
  104. );
  105. }
  106. let showGitHubActions = true;
  107. let showGitLabCICD = true;
  108. let showBitbucketPipelines = true;
  109. let showAzurePipelines = true;
  110. let showJenkins = true;
  111. if (projectBinding != null) {
  112. showGitHubActions = projectBinding.alm === AlmKeys.GitHub;
  113. showGitLabCICD = projectBinding.alm === AlmKeys.GitLab;
  114. showBitbucketPipelines = projectBinding.alm === AlmKeys.BitbucketCloud;
  115. showAzurePipelines = [AlmKeys.Azure, AlmKeys.GitHub].includes(projectBinding.alm);
  116. showJenkins = [
  117. AlmKeys.BitbucketCloud,
  118. AlmKeys.BitbucketServer,
  119. AlmKeys.GitHub,
  120. AlmKeys.GitLab,
  121. ].includes(projectBinding.alm);
  122. }
  123. return (
  124. <div className="sw-body-sm">
  125. <AnalysisStatus component={component} className="sw-mb-4 sw-w-max" />
  126. {selectedTutorial === undefined && (
  127. <div className="sw-flex sw-flex-col">
  128. <Title className="sw-mb-6 sw-heading-lg">
  129. {translate('onboarding.tutorial.page.title')}
  130. </Title>
  131. <LightPrimary>{translate('onboarding.tutorial.page.description')}</LightPrimary>
  132. <SubTitle className="sw-mt-12 sw-mb-4 sw-heading-md">
  133. {translate('onboarding.tutorial.choose_method')}
  134. </SubTitle>
  135. <div className="it__tutorial-selection sw-grid sw-gap-6 sw-grid-cols-12">
  136. {showJenkins &&
  137. renderAlm(
  138. TutorialModes.Jenkins,
  139. component.key,
  140. <img
  141. alt="" // Should be ignored by screen readers
  142. className="sw-h-4 sw-w-4"
  143. src={`${getBaseUrl()}/images/tutorials/jenkins.svg`}
  144. />,
  145. )}
  146. {showGitHubActions &&
  147. renderAlm(
  148. TutorialModes.GitHubActions,
  149. component.key,
  150. <img
  151. alt="" // Should be ignored by screen readers
  152. className="sw-h-4 sw-w-4"
  153. src={`${getBaseUrl()}/images/tutorials/github-actions.svg`}
  154. />,
  155. )}
  156. {showBitbucketPipelines &&
  157. renderAlm(
  158. TutorialModes.BitbucketPipelines,
  159. component.key,
  160. <img
  161. alt="" // Should be ignored by screen readers
  162. className="sw-h-4 sw-w-4"
  163. src={`${getBaseUrl()}/images/alm/bitbucket.svg`}
  164. />,
  165. )}
  166. {showGitLabCICD &&
  167. renderAlm(
  168. TutorialModes.GitLabCI,
  169. component.key,
  170. <img
  171. alt="" // Should be ignored by screen readers
  172. className="sw-h-4 sw-w-4"
  173. src={`${getBaseUrl()}/images/alm/gitlab.svg`}
  174. />,
  175. )}
  176. {showAzurePipelines &&
  177. renderAlm(
  178. TutorialModes.AzurePipelines,
  179. component.key,
  180. <img
  181. alt="" // Should be ignored by screen readers
  182. className="sw-h-4 sw-w-4"
  183. src={`${getBaseUrl()}/images/tutorials/azure-pipelines.svg`}
  184. />,
  185. )}
  186. {renderAlm(TutorialModes.OtherCI, component.key)}
  187. {renderAlm(TutorialModes.Local, component.key)}
  188. </div>
  189. </div>
  190. )}
  191. {selectedTutorial && (
  192. <Breadcrumbs className="sw-mb-3">
  193. <HoverLink to={getProjectTutorialLocation(component.key)}>
  194. {translate('onboarding.tutorial.breadcrumbs.home')}
  195. </HoverLink>
  196. <HoverLink to={getProjectTutorialLocation(component.key, selectedTutorial)}>
  197. {translate('onboarding.tutorial.breadcrumbs', selectedTutorial)}
  198. </HoverLink>
  199. </Breadcrumbs>
  200. )}
  201. {selectedTutorial === TutorialModes.Local && (
  202. <OtherTutorial component={component} baseUrl={baseUrl} isLocal currentUser={currentUser} />
  203. )}
  204. {selectedTutorial === TutorialModes.OtherCI && (
  205. <OtherTutorial component={component} baseUrl={baseUrl} currentUser={currentUser} />
  206. )}
  207. {selectedTutorial === TutorialModes.BitbucketPipelines && (
  208. <BitbucketPipelinesTutorial
  209. almBinding={almBinding}
  210. baseUrl={baseUrl}
  211. component={component}
  212. currentUser={currentUser}
  213. mainBranchName={mainBranchName}
  214. willRefreshAutomatically={willRefreshAutomatically}
  215. />
  216. )}
  217. {selectedTutorial === TutorialModes.GitHubActions && (
  218. <GitHubActionTutorial
  219. almBinding={almBinding}
  220. baseUrl={baseUrl}
  221. component={component}
  222. currentUser={currentUser}
  223. mainBranchName={mainBranchName}
  224. willRefreshAutomatically={willRefreshAutomatically}
  225. />
  226. )}
  227. {selectedTutorial === TutorialModes.Jenkins && (
  228. <JenkinsTutorial
  229. almBinding={almBinding}
  230. baseUrl={baseUrl}
  231. component={component}
  232. willRefreshAutomatically={willRefreshAutomatically}
  233. />
  234. )}
  235. {selectedTutorial === TutorialModes.GitLabCI && (
  236. <GitLabCITutorial
  237. baseUrl={baseUrl}
  238. component={component}
  239. currentUser={currentUser}
  240. willRefreshAutomatically={willRefreshAutomatically}
  241. />
  242. )}
  243. {selectedTutorial === TutorialModes.AzurePipelines && (
  244. <AzurePipelinesTutorial
  245. alm={projectBinding?.alm}
  246. baseUrl={baseUrl}
  247. component={component}
  248. currentUser={currentUser}
  249. willRefreshAutomatically={willRefreshAutomatically}
  250. />
  251. )}
  252. </div>
  253. );
  254. }