refactor RepositoryForm.tsx to FC

This commit is contained in:
Eduard Heimbuch
2020-11-24 12:50:22 +01:00
parent 240069734d
commit ff2b4d8acd
9 changed files with 355 additions and 197 deletions

View File

@@ -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"
}, },

View File

@@ -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"
}, },

View File

@@ -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} />

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) => {

View File

@@ -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}

View File

@@ -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);
}} }}
/> />

View File

@@ -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;
} }