3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 import { FlagMessage, InputField, Note, RequiredIcon, SubHeading, Switch } from 'design-system';
21 import * as React from 'react';
22 import { FormattedMessage } from 'react-intl';
23 import withAvailableFeatures, {
24 WithAvailableFeaturesProps,
25 } from '../../../../app/components/available-features/withAvailableFeatures';
26 import DocumentationLink from '../../../../components/common/DocumentationLink';
27 import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants';
28 import { translate } from '../../../../helpers/l10n';
29 import { convertGithubApiUrlToLink, stripTrailingSlash } from '../../../../helpers/urls';
33 ProjectAlmBindingResponse,
34 } from '../../../../types/alm-settings';
35 import { Feature } from '../../../../types/features';
36 import { Dict } from '../../../../types/types';
38 export interface AlmSpecificFormProps extends WithAvailableFeaturesProps {
40 instances: AlmSettingsInstance[];
41 formData: Omit<ProjectAlmBindingResponse, 'alm'>;
42 onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
45 interface LabelProps {
50 interface CommonFieldProps extends LabelProps {
52 helpParams?: Dict<string | JSX.Element>;
53 helpExample?: JSX.Element;
54 onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
55 propKey: keyof ProjectAlmBindingResponse;
58 function renderFieldWrapper(
59 label: React.ReactNode,
60 input: React.ReactNode,
61 help?: React.ReactNode,
64 <div className="sw-p-6 sw-flex sw-gap-12">
65 <div className="sw-w-abs-300">
66 <SubHeading>{label}</SubHeading>
67 {help && <div className="markdown">{help}</div>}
69 <div className="sw-flex-1">{input}</div>
74 function renderHelp({ help, helpExample, helpParams = {}, id }: CommonFieldProps) {
79 defaultMessage={translate('settings.pr_decoration.binding.form', id, 'help')}
80 id={`settings.pr_decoration.binding.form.${id}.help`}
84 <div className="sw-mt-2 sw-whitespace-nowrap">
85 {translate('example')}: <em>{helpExample}</em>
93 function renderLabel(props: LabelProps) {
94 const { optional, id } = props;
97 {translate('settings.pr_decoration.binding.form', id)}
98 {!optional && <RequiredIcon />}
103 function renderBooleanField(
104 props: Omit<CommonFieldProps, 'optional'> & {
106 inputExtra?: React.ReactNode;
109 const { id, value, onFieldChange, propKey, inputExtra } = props;
111 const label = translate('settings.pr_decoration.binding.form', id);
113 return renderFieldWrapper(
114 renderLabel({ ...props, optional: true }),
115 <div className="sw-flex sw-items-start">
118 labels={{ on: label, off: label }}
119 onChange={(v) => onFieldChange(propKey, v)}
122 {value == null && <Note className="sw-ml-2">{translate('settings.not_set')}</Note>}
129 function renderField(
130 props: CommonFieldProps & {
134 const { id, propKey, value, onFieldChange } = props;
135 return renderFieldWrapper(
141 onChange={(e) => onFieldChange(propKey, e.currentTarget.value)}
150 export function AlmSpecificForm(props: AlmSpecificFormProps) {
154 formData: { repository, slug, summaryCommentEnabled, monorepo },
157 let formFields: JSX.Element;
158 const instance = instances.find((i) => i.alm === alm);
166 helpExample: <strong>My Project</strong>,
168 onFieldChange: props.onFieldChange,
175 helpExample: <strong>My Repository</strong>,
176 id: 'azure.repository',
177 onFieldChange: props.onFieldChange,
178 propKey: 'repository',
179 value: repository || '',
184 case AlmKeys.BitbucketServer:
192 ? `${stripTrailingSlash(instance.url)}/projects/`
193 : 'https://bb.company.com/projects/'}
194 <strong>{'MY_PROJECT_KEY'}</strong>
195 {'/repos/my-repository-slug/browse'}
198 id: 'bitbucket.repository',
199 onFieldChange: props.onFieldChange,
200 propKey: 'repository',
201 value: repository || '',
209 ? `${stripTrailingSlash(instance.url)}/projects/MY_PROJECT_KEY/repos/`
210 : 'https://bb.company.com/projects/MY_PROJECT_KEY/repos/'}
211 <strong>{'my-repository-slug'}</strong>
215 id: 'bitbucket.slug',
216 onFieldChange: props.onFieldChange,
223 case AlmKeys.BitbucketCloud:
230 {'https://bitbucket.org/my-workspace/'}
231 <strong>{'my-repository-slug'}</strong>
234 id: 'bitbucketcloud.repository',
235 onFieldChange: props.onFieldChange,
236 propKey: 'repository',
237 value: repository || '',
250 ? `${stripTrailingSlash(convertGithubApiUrlToLink(instance.url))}/`
251 : 'https://github.com/'}
252 <strong>{'sonarsource/sonarqube'}</strong>
255 id: 'github.repository',
256 onFieldChange: props.onFieldChange,
257 propKey: 'repository',
258 value: repository || '',
261 {renderBooleanField({
263 id: 'github.summary_comment_setting',
264 onFieldChange: props.onFieldChange,
265 propKey: 'summaryCommentEnabled',
266 value: summaryCommentEnabled === undefined ? true : summaryCommentEnabled,
276 helpExample: <strong>123456</strong>,
277 id: 'gitlab.repository',
278 onFieldChange: props.onFieldChange,
279 propKey: 'repository',
280 value: repository || '',
287 const monorepoEnabled = props.hasFeature(Feature.MonoRepositoryPullRequestDecoration);
297 <DocumentationLink to={ALM_DOCUMENTATION_PATHS[alm]}>
298 {translate('learn_more')}
303 onFieldChange: props.onFieldChange,
306 inputExtra: monorepo && (
307 <FlagMessage className="sw-ml-2" variant="warning">
308 {translate('settings.pr_decoration.binding.form.monorepo.warning')}
316 export default withAvailableFeatures(AlmSpecificForm);