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.6KB

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