3 * Copyright (C) 2009-2023 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 * as React from 'react';
21 import { FormattedMessage } from 'react-intl';
22 import withAvailableFeatures, {
23 WithAvailableFeaturesProps,
24 } from '../../../../app/components/available-features/withAvailableFeatures';
25 import DocLink from '../../../../components/common/DocLink';
26 import Toggle from '../../../../components/controls/Toggle';
27 import { Alert } from '../../../../components/ui/Alert';
28 import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker';
29 import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants';
30 import { translate } from '../../../../helpers/l10n';
31 import { convertGithubApiUrlToLink, stripTrailingSlash } from '../../../../helpers/urls';
35 ProjectAlmBindingResponse,
36 } from '../../../../types/alm-settings';
37 import { Feature } from '../../../../types/features';
38 import { Dict } from '../../../../types/types';
40 export interface AlmSpecificFormProps extends WithAvailableFeaturesProps {
42 instances: AlmSettingsInstance[];
43 formData: Omit<ProjectAlmBindingResponse, 'alm'>;
44 onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
47 interface LabelProps {
52 interface CommonFieldProps extends LabelProps {
54 helpParams?: Dict<string | JSX.Element>;
55 helpExample?: JSX.Element;
56 onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
57 propKey: keyof ProjectAlmBindingResponse;
60 function renderFieldWrapper(
61 label: React.ReactNode,
62 input: React.ReactNode,
63 help?: React.ReactNode,
66 <div className="settings-definition">
67 <div className="settings-definition-left">
69 {help && <div className="markdown small spacer-top">{help}</div>}
71 <div className="settings-definition-right padded-top">{input}</div>
76 function renderHelp({ help, helpExample, helpParams = {}, id }: CommonFieldProps) {
81 defaultMessage={translate('settings.pr_decoration.binding.form', id, 'help')}
82 id={`settings.pr_decoration.binding.form.${id}.help`}
86 <div className="spacer-top nowrap">
87 {translate('example')}: <em>{helpExample}</em>
95 function renderLabel(props: LabelProps) {
96 const { optional, id } = props;
98 <label className="h3" htmlFor={id}>
99 {translate('settings.pr_decoration.binding.form', id)}
100 {!optional && <MandatoryFieldMarker />}
105 function renderBooleanField(
106 props: Omit<CommonFieldProps, 'optional'> & {
108 inputExtra?: React.ReactNode;
111 const { id, value, onFieldChange, propKey, inputExtra } = props;
112 return renderFieldWrapper(
113 renderLabel({ ...props, optional: true }),
114 <div className="display-flex-center big-spacer-top">
115 <div className="display-inline-block text-top">
116 <Toggle id={id} name={id} onChange={(v) => onFieldChange(propKey, v)} value={value} />
117 {value == null && <span className="spacer-left note">{translate('settings.not_set')}</span>}
125 function renderField(
126 props: CommonFieldProps & {
130 const { id, propKey, value, onFieldChange } = props;
131 return renderFieldWrapper(
134 className="input-super-large big-spacer-top"
138 onChange={(e) => onFieldChange(propKey, e.currentTarget.value)}
146 export function AlmSpecificForm(props: AlmSpecificFormProps) {
150 formData: { repository, slug, summaryCommentEnabled, monorepo },
153 let formFields: JSX.Element;
154 const instance = instances.find((i) => i.alm === alm);
162 helpExample: <strong>My Project</strong>,
164 onFieldChange: props.onFieldChange,
170 helpExample: <strong>My Repository</strong>,
171 id: 'azure.repository',
172 onFieldChange: props.onFieldChange,
173 propKey: 'repository',
174 value: repository || '',
179 case AlmKeys.BitbucketServer:
187 ? `${stripTrailingSlash(instance.url)}/projects/`
188 : 'https://bb.company.com/projects/'}
189 <strong>{'MY_PROJECT_KEY'}</strong>
190 {'/repos/my-repository-slug/browse'}
193 id: 'bitbucket.repository',
194 onFieldChange: props.onFieldChange,
195 propKey: 'repository',
196 value: repository || '',
203 ? `${stripTrailingSlash(instance.url)}/projects/MY_PROJECT_KEY/repos/`
204 : 'https://bb.company.com/projects/MY_PROJECT_KEY/repos/'}
205 <strong>{'my-repository-slug'}</strong>
209 id: 'bitbucket.slug',
210 onFieldChange: props.onFieldChange,
217 case AlmKeys.BitbucketCloud:
224 {'https://bitbucket.org/my-workspace/'}
225 <strong>{'my-repository-slug'}</strong>
228 id: 'bitbucketcloud.repository',
229 onFieldChange: props.onFieldChange,
230 propKey: 'repository',
231 value: repository || '',
244 ? `${stripTrailingSlash(convertGithubApiUrlToLink(instance.url))}/`
245 : 'https://github.com/'}
246 <strong>{'sonarsource/sonarqube'}</strong>
249 id: 'github.repository',
250 onFieldChange: props.onFieldChange,
251 propKey: 'repository',
252 value: repository || '',
254 {renderBooleanField({
256 id: 'github.summary_comment_setting',
257 onFieldChange: props.onFieldChange,
258 propKey: 'summaryCommentEnabled',
259 value: summaryCommentEnabled === undefined ? true : summaryCommentEnabled,
269 helpExample: <strong>123456</strong>,
270 id: 'gitlab.repository',
271 onFieldChange: props.onFieldChange,
272 propKey: 'repository',
273 value: repository || '',
280 const monorepoEnabled = props.hasFeature(Feature.MonoRepositoryPullRequestDecoration);
290 <DocLink to={ALM_DOCUMENTATION_PATHS[alm]}>{translate('learn_more')}</DocLink>
294 onFieldChange: props.onFieldChange,
297 inputExtra: monorepo && (
298 <Alert className="no-margin-bottom spacer-left" variant="warning" display="inline">
299 {translate('settings.pr_decoration.binding.form.monorepo.warning')}
307 export default withAvailableFeatures(AlmSpecificForm);