123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- /*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- import classNames from 'classnames';
- import {
- ButtonPrimary,
- ButtonSecondary,
- CloseIcon,
- FlagErrorIcon,
- FlagMessage,
- FlagSuccessIcon,
- FormField,
- InputField,
- InteractiveIcon,
- Link,
- Note,
- Title,
- } from 'design-system';
- import { isEmpty } from 'lodash';
- import * as React from 'react';
- import { FormattedMessage, useIntl } from 'react-intl';
- import { getValue } from '../../../../api/settings';
- import { useDocUrl } from '../../../../helpers/docs';
- import { translate } from '../../../../helpers/l10n';
- import { GlobalSettingKeys } from '../../../../types/settings';
- import { ImportProjectParam } from '../CreateProjectPage';
- import ProjectValidation, { ProjectData } from '../components/ProjectValidation';
- import { CreateProjectModes } from '../types';
-
- interface Props {
- branchesEnabled: boolean;
- onProjectSetupDone: (importProjects: ImportProjectParam) => void;
- onClose: () => void;
- }
-
- interface MainBranchState {
- mainBranchName: string;
- mainBranchNameError?: boolean;
- mainBranchNameTouched: boolean;
- }
-
- type ValidState = ProjectData & Required<Pick<ProjectData, 'key' | 'name'>>;
-
- export default function ManualProjectCreate(props: Readonly<Props>) {
- const [mainBranch, setMainBranch] = React.useState<MainBranchState>({
- mainBranchName: 'main',
- mainBranchNameTouched: false,
- });
- const [project, setProject] = React.useState<ProjectData>({
- hasError: false,
- key: '',
- name: '',
- touched: false,
- });
-
- const intl = useIntl();
- const docUrl = useDocUrl();
-
- React.useEffect(() => {
- async function fetchMainBranchName() {
- const { value: mainBranchName } = await getValue({ key: GlobalSettingKeys.MainBranchName });
-
- if (mainBranchName !== undefined) {
- setMainBranch((prevBranchName) => ({
- ...prevBranchName,
- mainBranchName,
- }));
- }
- }
-
- fetchMainBranchName();
- }, []);
-
- const canSubmit = (
- mainBranch: MainBranchState,
- projectData: ProjectData,
- ): projectData is ValidState => {
- const { mainBranchName } = mainBranch;
- const { key, name, hasError } = projectData;
- return Boolean(!hasError && !isEmpty(key) && !isEmpty(name) && !isEmpty(mainBranchName));
- };
-
- const handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
- event.preventDefault();
- if (canSubmit(mainBranch, project)) {
- props.onProjectSetupDone({
- creationMode: CreateProjectModes.Manual,
- monorepo: false,
- projects: [
- {
- project: project.key,
- name: (project.name ?? project.key).trim(),
- mainBranch: mainBranchName,
- },
- ],
- });
- }
- };
-
- const handleBranchNameChange = (mainBranchName: string, fromUI = false) => {
- setMainBranch({
- mainBranchName,
- mainBranchNameError: validateMainBranchName(mainBranchName),
- mainBranchNameTouched: fromUI,
- });
- };
-
- const validateMainBranchName = (mainBranchName: string) => {
- if (isEmpty(mainBranchName)) {
- return true;
- }
- return undefined;
- };
-
- const { mainBranchName, mainBranchNameError, mainBranchNameTouched } = mainBranch;
- const { branchesEnabled } = props;
-
- const mainBranchNameIsValid = mainBranchNameTouched && mainBranchNameError === undefined;
- const mainBranchNameIsInvalid = mainBranchNameTouched && mainBranchNameError !== undefined;
-
- return (
- <section
- aria-label={translate('onboarding.create_project.manual.title')}
- className="sw-body-sm"
- >
- <div className="sw-flex sw-justify-between">
- <FormattedMessage
- id="onboarding.create_project.manual.step1"
- defaultMessage={translate('onboarding.create_project.manual.step1')}
- />
- <InteractiveIcon
- Icon={CloseIcon}
- aria-label={intl.formatMessage({ id: 'clear' })}
- currentColor
- onClick={props.onClose}
- size="small"
- />
- </div>
- <Title>{translate('onboarding.create_project.manual.title')}</Title>
- {branchesEnabled && (
- <FlagMessage className="sw-my-4" variant="info">
- {translate('onboarding.create_project.pr_decoration.information')}
- </FlagMessage>
- )}
- <div className="sw-max-w-[50%] sw-mt-2">
- <form
- id="create-project-manual"
- className="sw-flex-col sw-body-sm"
- onSubmit={handleFormSubmit}
- >
- <ProjectValidation onChange={setProject} />
-
- <FormField
- htmlFor="main-branch-name"
- label={translate('onboarding.create_project.main_branch_name')}
- required
- >
- <div>
- <InputField
- className={classNames({
- 'js__is-invalid': mainBranchNameIsInvalid,
- })}
- size="large"
- id="main-branch-name"
- minLength={1}
- onChange={(e) => handleBranchNameChange(e.currentTarget.value, true)}
- type="text"
- value={mainBranchName}
- isInvalid={mainBranchNameIsInvalid}
- isValid={mainBranchNameIsValid}
- required
- />
- {mainBranchNameIsInvalid && <FlagErrorIcon className="sw-ml-2" />}
- {mainBranchNameIsValid && <FlagSuccessIcon className="sw-ml-2" />}
- </div>
- <Note className="sw-mt-2">
- <FormattedMessage
- id="onboarding.create_project.main_branch_name.description"
- defaultMessage={translate('onboarding.create_project.main_branch_name.description')}
- values={{
- learn_more: (
- <Link to={docUrl('/analyzing-source-code/branches/branch-analysis')}>
- {translate('learn_more')}
- </Link>
- ),
- }}
- />
- </Note>
- </FormField>
-
- <ButtonSecondary className="sw-mt-4 sw-mr-4" onClick={props.onClose} type="button">
- {intl.formatMessage({ id: 'cancel' })}
- </ButtonSecondary>
- <ButtonPrimary
- className="sw-mt-4"
- type="submit"
- disabled={!canSubmit(mainBranch, project)}
- >
- {translate('next')}
- </ButtonPrimary>
- </form>
- </div>
- </section>
- );
- }
|