mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 16:35:45 +01:00
refactor RepositoryForm.tsx to FC
This commit is contained in:
@@ -22,7 +22,10 @@
|
|||||||
"typeHelpText": "Der Typ des Repository (Mercurial, Git oder Subversion).",
|
"typeHelpText": "Der Typ des Repository (Mercurial, Git oder Subversion).",
|
||||||
"contactHelpText": "E-Mail Adresse der Person, die für das Repository verantwortlich ist.",
|
"contactHelpText": "E-Mail Adresse der Person, die für das Repository verantwortlich ist.",
|
||||||
"descriptionHelpText": "Eine kurze Beschreibung des Repository.",
|
"descriptionHelpText": "Eine kurze Beschreibung des Repository.",
|
||||||
"initializeRepository": "Erstellt einen ersten Branch und committet eine README.md."
|
"initializeRepository": "Erstellt einen ersten Branch und committet eine README.md.",
|
||||||
|
"importUrlHelpText": "Importiert das gesamte Repository inkl. aller Branches und Tags über die Remote URL.",
|
||||||
|
"usernameHelpText": "Benutzername könnte für den Import benötigt werden. Wird ignoriert, falls nicht gesetzt.",
|
||||||
|
"passwordHelpText": "Password könnte für den Import benötigt werden. Wird ignoriert, falls nicht gesetzt."
|
||||||
},
|
},
|
||||||
"repositoryRoot": {
|
"repositoryRoot": {
|
||||||
"errorTitle": "Fehler",
|
"errorTitle": "Fehler",
|
||||||
@@ -46,7 +49,13 @@
|
|||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Repository hinzufügen",
|
"title": "Repository hinzufügen",
|
||||||
"subtitle": "Erstellen eines neuen Repository"
|
"createSubtitle": "Neues Repository erstellen",
|
||||||
|
"importSubtitle": "Bestehendes Repository importieren",
|
||||||
|
"importUrl": "Remote repository url",
|
||||||
|
"username": "Benutzername",
|
||||||
|
"password": "Passwort",
|
||||||
|
"createButton": "Neues Repository erstellen",
|
||||||
|
"importButton": "Repository importieren"
|
||||||
},
|
},
|
||||||
"branches": {
|
"branches": {
|
||||||
"overview": {
|
"overview": {
|
||||||
@@ -155,7 +164,8 @@
|
|||||||
},
|
},
|
||||||
"repositoryForm": {
|
"repositoryForm": {
|
||||||
"subtitle": "Repository bearbeiten",
|
"subtitle": "Repository bearbeiten",
|
||||||
"submit": "Speichern",
|
"submitCreate": "Speichern",
|
||||||
|
"submitImport": "Importieren",
|
||||||
"initializeRepository": "Repository initiieren",
|
"initializeRepository": "Repository initiieren",
|
||||||
"dangerZone": "Umbenennen und Löschen"
|
"dangerZone": "Umbenennen und Löschen"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,7 +22,10 @@
|
|||||||
"typeHelpText": "The type of the repository (e.g. Mercurial, Git or Subversion).",
|
"typeHelpText": "The type of the repository (e.g. Mercurial, Git or Subversion).",
|
||||||
"contactHelpText": "Email address of the person who is responsible for this repository.",
|
"contactHelpText": "Email address of the person who is responsible for this repository.",
|
||||||
"descriptionHelpText": "A short description of the repository.",
|
"descriptionHelpText": "A short description of the repository.",
|
||||||
"initializeRepository": "Creates an initial branch and commits a basic README.md."
|
"initializeRepository": "Creates an initial branch and commits a basic README.md.",
|
||||||
|
"importUrlHelpText": "Import the whole repository including all branches and tags via remote url",
|
||||||
|
"usernameHelpText": "Username may be required to import the remote repository. Will be ignored if not provided.",
|
||||||
|
"passwordHelpText": "Password may be required to import the remote repository. Will be ignored if not provided."
|
||||||
},
|
},
|
||||||
"repositoryRoot": {
|
"repositoryRoot": {
|
||||||
"errorTitle": "Error",
|
"errorTitle": "Error",
|
||||||
@@ -46,7 +49,13 @@
|
|||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Add Repository",
|
"title": "Add Repository",
|
||||||
"subtitle": "Create a new repository"
|
"createSubtitle": "Create a new repository",
|
||||||
|
"importSubtitle": "Import existing repository",
|
||||||
|
"importUrl": "Remote repository url",
|
||||||
|
"username": "Username",
|
||||||
|
"password": "Password",
|
||||||
|
"createButton": "Create new repository",
|
||||||
|
"importButton": "Import repository"
|
||||||
},
|
},
|
||||||
"branches": {
|
"branches": {
|
||||||
"overview": {
|
"overview": {
|
||||||
@@ -155,7 +164,8 @@
|
|||||||
},
|
},
|
||||||
"repositoryForm": {
|
"repositoryForm": {
|
||||||
"subtitle": "Edit Repository",
|
"subtitle": "Edit Repository",
|
||||||
"submit": "Save",
|
"submitCreate": "Save",
|
||||||
|
"submitImport": "Import",
|
||||||
"initializeRepository": "Initialize repository",
|
"initializeRepository": "Initialize repository",
|
||||||
"dangerZone": "Rename and delete"
|
"dangerZone": "Rename and delete"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ class Main extends React.Component<Props> {
|
|||||||
<Redirect exact strict from="/repos" to="/repos/" />
|
<Redirect exact strict from="/repos" to="/repos/" />
|
||||||
<ProtectedRoute exact path="/repos/" component={Overview} authenticated={authenticated} />
|
<ProtectedRoute exact path="/repos/" component={Overview} authenticated={authenticated} />
|
||||||
<ProtectedRoute exact path="/repos/create" component={Create} authenticated={authenticated} />
|
<ProtectedRoute exact path="/repos/create" component={Create} authenticated={authenticated} />
|
||||||
|
<ProtectedRoute exact path="/repos/import" component={Create} authenticated={authenticated} />
|
||||||
<ProtectedRoute exact path="/repos/:namespace" component={Overview} authenticated={authenticated} />
|
<ProtectedRoute exact path="/repos/:namespace" component={Overview} authenticated={authenticated} />
|
||||||
<ProtectedRoute exact path="/repos/:namespace/:page" component={Overview} authenticated={authenticated} />
|
<ProtectedRoute exact path="/repos/:namespace/:page" component={Overview} authenticated={authenticated} />
|
||||||
<ProtectedRoute path="/repo/:namespace/:name" component={RepositoryRoot} authenticated={authenticated} />
|
<ProtectedRoute path="/repo/:namespace/:name" component={RepositoryRoot} authenticated={authenticated} />
|
||||||
|
|||||||
@@ -21,14 +21,16 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React, { FC, useEffect, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { WithTranslation, withTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
import { Repository, RepositoryCreation, RepositoryType } from "@scm-manager/ui-types";
|
import { Repository, RepositoryType } from "@scm-manager/ui-types";
|
||||||
import { Checkbox, InputField, Level, Select, SubmitButton, Subtitle, Textarea } from "@scm-manager/ui-components";
|
import { Checkbox, InputField, Level, Select, SubmitButton, Textarea } from "@scm-manager/ui-components";
|
||||||
import * as validator from "./repositoryValidation";
|
import * as validator from "./repositoryValidation";
|
||||||
import { CUSTOM_NAMESPACE_STRATEGY } from "../../modules/repos";
|
import { CUSTOM_NAMESPACE_STRATEGY } from "../../modules/repos";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import RepositoryFormSwitcher from "./RepositoryFormSwitcher";
|
||||||
|
|
||||||
const CheckboxWrapper = styled.div`
|
const CheckboxWrapper = styled.div`
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
@@ -44,8 +46,18 @@ const SpaceBetween = styled.div`
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = WithTranslation & {
|
const Column = styled.div`
|
||||||
submitForm: (repo: RepositoryCreation, shouldInit: boolean) => void;
|
padding: 0 0.75rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Columns = styled.div`
|
||||||
|
padding: 0.75rem 0 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
createRepository?: (repo: RepositoryCreation, shouldInit: boolean) => void;
|
||||||
|
modifyRepository?: (repo: RepositoryCreation) => void;
|
||||||
|
importRepository?: (repo: RepositoryCreation) => void;
|
||||||
repository?: Repository;
|
repository?: Repository;
|
||||||
repositoryTypes?: RepositoryType[];
|
repositoryTypes?: RepositoryType[];
|
||||||
namespaceStrategy?: string;
|
namespaceStrategy?: string;
|
||||||
@@ -53,20 +65,21 @@ type Props = WithTranslation & {
|
|||||||
indexResources: any;
|
indexResources: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type RepositoryCreation = Repository & {
|
||||||
repository: RepositoryCreation;
|
contextEntries: object;
|
||||||
initRepository: boolean;
|
|
||||||
namespaceValidationError: boolean;
|
|
||||||
nameValidationError: boolean;
|
|
||||||
contactValidationError: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RepositoryForm extends React.Component<Props, State> {
|
const RepositoryForm: FC<Props> = ({
|
||||||
constructor(props: Props) {
|
createRepository,
|
||||||
super(props);
|
modifyRepository,
|
||||||
|
importRepository,
|
||||||
this.state = {
|
repository,
|
||||||
repository: {
|
repositoryTypes,
|
||||||
|
namespaceStrategy,
|
||||||
|
loading,
|
||||||
|
indexResources
|
||||||
|
}) => {
|
||||||
|
const [repo, setRepo] = useState<RepositoryCreation>({
|
||||||
name: "",
|
name: "",
|
||||||
namespace: "",
|
namespace: "",
|
||||||
type: "",
|
type: "",
|
||||||
@@ -74,120 +87,95 @@ class RepositoryForm extends React.Component<Props, State> {
|
|||||||
description: "",
|
description: "",
|
||||||
contextEntries: {},
|
contextEntries: {},
|
||||||
_links: {}
|
_links: {}
|
||||||
},
|
|
||||||
initRepository: false,
|
|
||||||
namespaceValidationError: false,
|
|
||||||
nameValidationError: false,
|
|
||||||
contactValidationError: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { repository } = this.props;
|
|
||||||
if (repository) {
|
|
||||||
this.setState({
|
|
||||||
repository: {
|
|
||||||
...repository,
|
|
||||||
contextEntries: {}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
const [initRepository, setInitRepository] = useState(false);
|
||||||
}
|
const [namespaceValidationError, setNamespaceValidationError] = useState(false);
|
||||||
|
const [nameValidationError, setNameValidationError] = useState(false);
|
||||||
|
const [contactValidationError, setContactValidationError] = useState(false);
|
||||||
|
const [importUrl, setImportUrl] = useState("");
|
||||||
|
const [username, setUsername] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
|
||||||
isFalsy(value: string) {
|
const location = useLocation();
|
||||||
return !value;
|
const [t] = useTranslation("repos");
|
||||||
}
|
|
||||||
|
|
||||||
isValid = () => {
|
useEffect(() => {
|
||||||
const { namespaceStrategy } = this.props;
|
if (repository) {
|
||||||
const { repository } = this.state;
|
setRepo({ ...repository, contextEntries: {} });
|
||||||
|
}
|
||||||
|
}, [repository]);
|
||||||
|
|
||||||
|
const isValid = () => {
|
||||||
return !(
|
return !(
|
||||||
this.state.namespaceValidationError ||
|
namespaceValidationError ||
|
||||||
this.state.nameValidationError ||
|
nameValidationError ||
|
||||||
this.state.contactValidationError ||
|
contactValidationError ||
|
||||||
this.isFalsy(repository.name) ||
|
!repo.name ||
|
||||||
(namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY && this.isFalsy(repository.namespace))
|
(namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY && !repo.namespace) ||
|
||||||
|
(isImportPage() && !importUrl)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
submit = (event: React.FormEvent<HTMLFormElement>) => {
|
const submit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (this.isValid()) {
|
const submitForm = evaluateSubmit();
|
||||||
this.props.submitForm(this.state.repository, this.state.initRepository);
|
if (isValid() && submitForm) {
|
||||||
|
submitForm(repo, initRepository);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
isCreateMode = () => {
|
const evaluateSubmit = () => {
|
||||||
return !this.props.repository;
|
if (isImportPage()) {
|
||||||
|
return importRepository;
|
||||||
|
} else if (isCreatePage()) {
|
||||||
|
return createRepository;
|
||||||
|
} else {
|
||||||
|
return modifyRepository;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
isModifiable = () => {
|
const isEditMode = () => {
|
||||||
return !!this.props.repository && !!this.props.repository._links.update;
|
return !!repository;
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleInitCheckbox = () => {
|
const isModifiable = () => {
|
||||||
this.setState({
|
return !!repository && !!repository._links.update;
|
||||||
initRepository: !this.state.initRepository
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setCreationContextEntry = (key: string, value: any) => {
|
const toggleInitCheckbox = () => {
|
||||||
this.setState({
|
setInitRepository(!initRepository);
|
||||||
repository: {
|
};
|
||||||
...this.state.repository,
|
|
||||||
|
const setCreationContextEntry = (key: string, value: any) => {
|
||||||
|
setRepo({
|
||||||
|
...repo,
|
||||||
contextEntries: {
|
contextEntries: {
|
||||||
...this.state.repository.contextEntries,
|
...repo.contextEntries,
|
||||||
[key]: value
|
[key]: value
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
const resolveLocation = () => {
|
||||||
const { loading, t } = this.props;
|
const currentUrl = location.pathname;
|
||||||
const repository = this.state.repository;
|
if (currentUrl.includes("/repos/create")) {
|
||||||
|
return "create";
|
||||||
const disabled = !this.isModifiable() && !this.isCreateMode();
|
|
||||||
|
|
||||||
const submitButton = disabled ? null : (
|
|
||||||
<Level right={<SubmitButton disabled={!this.isValid()} loading={loading} label={t("repositoryForm.submit")} />} />
|
|
||||||
);
|
|
||||||
|
|
||||||
let subtitle = null;
|
|
||||||
if (this.props.repository) {
|
|
||||||
// edit existing repo
|
|
||||||
subtitle = <Subtitle subtitle={t("repositoryForm.subtitle")} />;
|
|
||||||
}
|
}
|
||||||
|
if (currentUrl.includes("/repos/import")) {
|
||||||
return (
|
return "import";
|
||||||
<>
|
|
||||||
{subtitle}
|
|
||||||
<form onSubmit={this.submit}>
|
|
||||||
{this.renderCreateOnlyFields()}
|
|
||||||
<InputField
|
|
||||||
label={t("repository.contact")}
|
|
||||||
onChange={this.handleContactChange}
|
|
||||||
value={repository ? repository.contact : ""}
|
|
||||||
validationError={this.state.contactValidationError}
|
|
||||||
errorMessage={t("validation.contact-invalid")}
|
|
||||||
helpText={t("help.contactHelpText")}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Textarea
|
|
||||||
label={t("repository.description")}
|
|
||||||
onChange={this.handleDescriptionChange}
|
|
||||||
value={repository ? repository.description : ""}
|
|
||||||
helpText={t("help.descriptionHelpText")}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
{submitButton}
|
|
||||||
</form>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
createSelectOptions(repositoryTypes?: RepositoryType[]) {
|
const isImportPage = () => {
|
||||||
|
return resolveLocation() === "import";
|
||||||
|
};
|
||||||
|
|
||||||
|
const isCreatePage = () => {
|
||||||
|
return resolveLocation() === "create";
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSelectOptions = (repositoryTypes?: RepositoryType[]) => {
|
||||||
if (repositoryTypes) {
|
if (repositoryTypes) {
|
||||||
return repositoryTypes.map(repositoryType => {
|
return repositoryTypes.map(repositoryType => {
|
||||||
return {
|
return {
|
||||||
@@ -197,18 +185,16 @@ class RepositoryForm extends React.Component<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
};
|
||||||
|
|
||||||
renderNamespaceField = () => {
|
const renderNamespaceField = () => {
|
||||||
const { namespaceStrategy, t } = this.props;
|
|
||||||
const repository = this.state.repository;
|
|
||||||
const props = {
|
const props = {
|
||||||
label: t("repository.namespace"),
|
label: t("repository.namespace"),
|
||||||
helpText: t("help.namespaceHelpText"),
|
helpText: t("help.namespaceHelpText"),
|
||||||
value: repository ? repository.namespace : "",
|
value: repo ? repo.namespace : "",
|
||||||
onChange: this.handleNamespaceChange,
|
onChange: handleNamespaceChange,
|
||||||
errorMessage: t("validation.namespace-invalid"),
|
errorMessage: t("validation.namespace-invalid"),
|
||||||
validationError: this.state.namespaceValidationError
|
validationError: namespaceValidationError
|
||||||
};
|
};
|
||||||
|
|
||||||
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY) {
|
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY) {
|
||||||
@@ -218,25 +204,63 @@ class RepositoryForm extends React.Component<Props, State> {
|
|||||||
return <ExtensionPoint name="repos.create.namespace" props={props} renderAll={false} />;
|
return <ExtensionPoint name="repos.create.namespace" props={props} renderAll={false} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderCreateOnlyFields() {
|
const renderUrlImportFields = () => {
|
||||||
if (!this.isCreateMode()) {
|
if (!isImportPage()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Columns className="columns is-multiline">
|
||||||
|
<Column className="column is-full">
|
||||||
|
<InputField
|
||||||
|
label={t("create.importUrl")}
|
||||||
|
onChange={handleImportUrlChange}
|
||||||
|
value={importUrl}
|
||||||
|
helpText={t("help.importUrlHelpText")}
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
<Column className="column is-half">
|
||||||
|
<InputField
|
||||||
|
label={t("create.username")}
|
||||||
|
onChange={setUsername}
|
||||||
|
value={username}
|
||||||
|
helpText={t("help.usernameHelpText")}
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
<Column className="column is-half">
|
||||||
|
<InputField
|
||||||
|
label={t("create.password")}
|
||||||
|
onChange={setPassword}
|
||||||
|
value={password}
|
||||||
|
type="password"
|
||||||
|
helpText={t("help.passwordHelpText")}
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
</Columns>
|
||||||
|
<hr />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderCreateOnlyFields = () => {
|
||||||
|
if (isEditMode()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { repositoryTypes, indexResources, t } = this.props;
|
|
||||||
const repository = this.state.repository;
|
|
||||||
const extensionProps = {
|
const extensionProps = {
|
||||||
repository,
|
repository: repo,
|
||||||
setCreationContextEntry: this.setCreationContextEntry,
|
setCreationContextEntry: setCreationContextEntry,
|
||||||
indexResources
|
indexResources
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{this.renderNamespaceField()}
|
{renderUrlImportFields()}
|
||||||
|
{renderNamespaceField()}
|
||||||
<InputField
|
<InputField
|
||||||
label={t("repository.name")}
|
label={t("repository.name")}
|
||||||
onChange={this.handleNameChange}
|
onChange={handleNameChange}
|
||||||
value={repository ? repository.name : ""}
|
value={repo ? repo.name : ""}
|
||||||
validationError={this.state.nameValidationError}
|
validationError={nameValidationError}
|
||||||
errorMessage={t("validation.name-invalid")}
|
errorMessage={t("validation.name-invalid")}
|
||||||
helpText={t("help.nameHelpText")}
|
helpText={t("help.nameHelpText")}
|
||||||
/>
|
/>
|
||||||
@@ -244,75 +268,94 @@ class RepositoryForm extends React.Component<Props, State> {
|
|||||||
<SelectWrapper>
|
<SelectWrapper>
|
||||||
<Select
|
<Select
|
||||||
label={t("repository.type")}
|
label={t("repository.type")}
|
||||||
onChange={this.handleTypeChange}
|
onChange={type => setRepo({ ...repo, type })}
|
||||||
value={repository ? repository.type : ""}
|
value={repo ? repo.type : ""}
|
||||||
options={this.createSelectOptions(repositoryTypes)}
|
options={createSelectOptions(repositoryTypes)}
|
||||||
helpText={t("help.typeHelpText")}
|
helpText={t("help.typeHelpText")}
|
||||||
/>
|
/>
|
||||||
</SelectWrapper>
|
</SelectWrapper>
|
||||||
|
{!isImportPage() && (
|
||||||
<CheckboxWrapper>
|
<CheckboxWrapper>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={t("repositoryForm.initializeRepository")}
|
label={t("repositoryForm.initializeRepository")}
|
||||||
checked={this.state.initRepository}
|
checked={initRepository}
|
||||||
onChange={this.toggleInitCheckbox}
|
onChange={toggleInitCheckbox}
|
||||||
helpText={t("help.initializeRepository")}
|
helpText={t("help.initializeRepository")}
|
||||||
/>
|
/>
|
||||||
{this.state.initRepository && (
|
{initRepository && (
|
||||||
<ExtensionPoint name="repos.create.initialize" props={extensionProps} renderAll={true} />
|
<ExtensionPoint name="repos.create.initialize" props={extensionProps} renderAll={true} />
|
||||||
)}
|
)}
|
||||||
</CheckboxWrapper>
|
</CheckboxWrapper>
|
||||||
|
)}
|
||||||
</SpaceBetween>
|
</SpaceBetween>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
handleNamespaceChange = (namespace: string) => {
|
|
||||||
this.setState({
|
|
||||||
namespaceValidationError: !validator.isNamespaceValid(namespace),
|
|
||||||
repository: {
|
|
||||||
...this.state.repository,
|
|
||||||
namespace
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleNameChange = (name: string) => {
|
const handleNamespaceChange = (namespace: string) => {
|
||||||
this.setState({
|
setNamespaceValidationError(!validator.isNamespaceValid(namespace));
|
||||||
nameValidationError: !validator.isNameValid(name),
|
setRepo({ ...repo, namespace });
|
||||||
repository: {
|
|
||||||
...this.state.repository,
|
|
||||||
name
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleTypeChange = (type: string) => {
|
const handleNameChange = (name: string) => {
|
||||||
this.setState({
|
setNameValidationError(!validator.isNameValid(name));
|
||||||
repository: {
|
setRepo({ ...repo, name });
|
||||||
...this.state.repository,
|
|
||||||
type
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleContactChange = (contact: string) => {
|
const handleContactChange = (contact: string) => {
|
||||||
this.setState({
|
setContactValidationError(!validator.isContactValid(contact));
|
||||||
contactValidationError: !validator.isContactValid(contact),
|
setRepo({ ...repo, contact });
|
||||||
repository: {
|
|
||||||
...this.state.repository,
|
|
||||||
contact
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDescriptionChange = (description: string) => {
|
const handleImportUrlChange = (url: string) => {
|
||||||
this.setState({
|
if (!repo.name) {
|
||||||
repository: {
|
const match = url.match(/([^\/]+)\.git/i);
|
||||||
...this.state.repository,
|
if (match && match[1]) {
|
||||||
description
|
setRepo({ ...repo, name: match[1] });
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
setImportUrl(url);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default withTranslation("repos")(RepositoryForm);
|
const disabled = !isModifiable() && isEditMode();
|
||||||
|
|
||||||
|
const getSubmitButtonTranslationKey = () => {
|
||||||
|
return isImportPage() ? "repositoryForm.submitImport" : "repositoryForm.submitCreate";
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitButton = disabled ? null : (
|
||||||
|
<Level
|
||||||
|
right={<SubmitButton disabled={!isValid()} loading={loading} label={t(getSubmitButtonTranslationKey())} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!isEditMode() && (
|
||||||
|
<RepositoryFormSwitcher repository={repository} createMode={isImportPage() ? "IMPORT" : "CREATE"} />
|
||||||
|
)}
|
||||||
|
<form onSubmit={submit}>
|
||||||
|
{renderCreateOnlyFields()}
|
||||||
|
<InputField
|
||||||
|
label={t("repository.contact")}
|
||||||
|
onChange={handleContactChange}
|
||||||
|
value={repo ? repo.contact : ""}
|
||||||
|
validationError={contactValidationError}
|
||||||
|
errorMessage={t("validation.contact-invalid")}
|
||||||
|
helpText={t("help.contactHelpText")}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
<Textarea
|
||||||
|
label={t("repository.description")}
|
||||||
|
onChange={description => setRepo({ ...repo, description })}
|
||||||
|
value={repo ? repo.description : ""}
|
||||||
|
helpText={t("help.descriptionHelpText")}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
{submitButton}
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RepositoryForm;
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Button, ButtonAddons, Level, Subtitle } from "@scm-manager/ui-components";
|
||||||
|
import { Repository } from "@scm-manager/ui-types";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
repository?: Repository;
|
||||||
|
createMode: "CREATE" | "IMPORT";
|
||||||
|
};
|
||||||
|
|
||||||
|
const TopLevel = styled(Level)`
|
||||||
|
margin-top: -2rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SmallButton = styled(Button)`
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RepositoryFormSwitcher: FC<Props> = ({ repository, createMode }) => {
|
||||||
|
const [t] = useTranslation("repos");
|
||||||
|
|
||||||
|
const isImportMode = () => {
|
||||||
|
return createMode === "IMPORT";
|
||||||
|
};
|
||||||
|
|
||||||
|
const isCreateMode = () => {
|
||||||
|
return createMode === "CREATE";
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderSubtitle = () => {
|
||||||
|
let subtitle;
|
||||||
|
if (repository) {
|
||||||
|
subtitle = "repositoryForm.subtitle";
|
||||||
|
} else if (isImportMode()) {
|
||||||
|
subtitle = "create.importSubtitle";
|
||||||
|
} else {
|
||||||
|
subtitle = "create.createSubtitle";
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Subtitle subtitle={t(subtitle)} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TopLevel
|
||||||
|
left={renderSubtitle()}
|
||||||
|
right={
|
||||||
|
<ButtonAddons>
|
||||||
|
<SmallButton
|
||||||
|
label={t("create.createButton")}
|
||||||
|
icon="fa fa-plus"
|
||||||
|
color={isCreateMode() ? "link is-selected" : undefined}
|
||||||
|
link={isImportMode() ? "/repos/create" : undefined}
|
||||||
|
/>
|
||||||
|
<SmallButton
|
||||||
|
label={t("create.importButton")}
|
||||||
|
icon="fa fa-file-upload"
|
||||||
|
color={isImportMode() ? "link is-selected" : undefined}
|
||||||
|
link={isCreateMode() ? "/repos/import" : undefined}
|
||||||
|
/>
|
||||||
|
</ButtonAddons>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RepositoryFormSwitcher;
|
||||||
@@ -25,10 +25,11 @@
|
|||||||
import { validation } from "@scm-manager/ui-components";
|
import { validation } from "@scm-manager/ui-components";
|
||||||
|
|
||||||
const nameRegex = /(?!^\.\.$)(?!^\.$)(?!.*[.]git$)(?!.*[\\\[\]])^[A-Za-z0-9\.][A-Za-z0-9\.\-_]*$/;
|
const nameRegex = /(?!^\.\.$)(?!^\.$)(?!.*[.]git$)(?!.*[\\\[\]])^[A-Za-z0-9\.][A-Za-z0-9\.\-_]*$/;
|
||||||
const namespaceExceptionsRegex = /^(([0-9]{1,3})|(create))$/;
|
const namespaceExceptionsRegexCreate = /^(([0-9]{1,3})|(create))$/;
|
||||||
|
const namespaceExceptionsRegexImport = /^(([0-9]{1,3})|(import))$/;
|
||||||
|
|
||||||
export const isNamespaceValid = (name: string) => {
|
export const isNamespaceValid = (name: string) => {
|
||||||
return nameRegex.test(name) && !namespaceExceptionsRegex.test(name);
|
return nameRegex.test(name) && !namespaceExceptionsRegexCreate.test(name) && !namespaceExceptionsRegexImport.test(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isNameValid = (name: string) => {
|
export const isNameValid = (name: string) => {
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ class Create extends React.Component<Props> {
|
|||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
title={t("create.title")}
|
title={t("create.title")}
|
||||||
subtitle={t("create.subtitle")}
|
|
||||||
loading={pageLoading}
|
loading={pageLoading}
|
||||||
error={error}
|
error={error}
|
||||||
showContentOnError={true}
|
showContentOnError={true}
|
||||||
@@ -104,7 +103,7 @@ class Create extends React.Component<Props> {
|
|||||||
repositoryTypes={repositoryTypes}
|
repositoryTypes={repositoryTypes}
|
||||||
loading={createLoading}
|
loading={createLoading}
|
||||||
namespaceStrategy={namespaceStrategies.current}
|
namespaceStrategy={namespaceStrategies.current}
|
||||||
submitForm={(repo, initRepository) => {
|
createRepository={(repo, initRepository) => {
|
||||||
createRepo(repoLink, repo, initRepository, (repo: Repository) => this.repoCreated(repo));
|
createRepo(repoLink, repo, initRepository, (repo: Repository) => this.repoCreated(repo));
|
||||||
}}
|
}}
|
||||||
indexResources={indexResources}
|
indexResources={indexResources}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class EditRepo extends React.Component<Props> {
|
|||||||
<RepositoryForm
|
<RepositoryForm
|
||||||
repository={this.props.repository}
|
repository={this.props.repository}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
submitForm={repo => {
|
createRepository={repo => {
|
||||||
this.props.modifyRepo(repo, this.repoModified);
|
this.props.modifyRepo(repo, this.repoModified);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import React from "react";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { RouteComponentProps, withRouter } from "react-router-dom";
|
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||||
import { WithTranslation, withTranslation } from "react-i18next";
|
import { WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { NamespaceCollection, RepositoryCollection } from "@scm-manager/ui-types";
|
import { NamespaceCollection, RepositoryCollection, Link } from "@scm-manager/ui-types";
|
||||||
import {
|
import {
|
||||||
CreateButton,
|
CreateButton,
|
||||||
LinkPaginator,
|
LinkPaginator,
|
||||||
@@ -95,7 +95,8 @@ class Overview extends React.Component<Props> {
|
|||||||
getReposLink = () => {
|
getReposLink = () => {
|
||||||
const { namespace, namespaces, reposLink } = this.props;
|
const { namespace, namespaces, reposLink } = this.props;
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
return namespaces?._embedded.namespaces.find(n => n.namespace === namespace)?._links?.repositories?.href;
|
return (namespaces?._embedded.namespaces.find(n => n.namespace === namespace)?._links?.repositories as Link)
|
||||||
|
?.href;
|
||||||
} else {
|
} else {
|
||||||
return reposLink;
|
return reposLink;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user