refactor frontend components

This commit is contained in:
Eduard Heimbuch
2020-11-30 11:32:11 +01:00
parent 7ee8755dca
commit 6f2dda4fe4
7 changed files with 299 additions and 63 deletions

View File

@@ -22,11 +22,12 @@
* SOFTWARE.
*/
import { Collection } from "./hal";
import {Collection, Links} from "./hal";
export type RepositoryType = {
name: string;
displayName: string;
_links: Links;
};
export type RepositoryTypeCollection = Collection & {

View File

@@ -0,0 +1,65 @@
/*
* 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 { RepositoryType } from "@scm-manager/ui-types";
import { useTranslation } from "react-i18next";
import { Select } from "@scm-manager/ui-components";
type Props = {
repositoryTypes: RepositoryType[];
repositoryType?: RepositoryType;
setRepositoryType: (repositoryType: RepositoryType) => void;
};
const ImportRepositoryTypeSelect: FC<Props> = ({ repositoryTypes, repositoryType, setRepositoryType }) => {
const [t] = useTranslation("repos");
const createSelectOptions = () => {
const options = repositoryTypes.filter(repoType => !!repoType._links.import).map(repositoryType => {
return {
label: repositoryType.displayName,
value: repositoryType.name
};
});
options.unshift({ label: "", value: "" });
return options;
};
const onChangeType = (type: string) => {
const repositoryType = repositoryTypes.filter(t => t.name === type)[0];
setRepositoryType(repositoryType);
};
return (
<Select
label={t("repository.type")}
onChange={onChangeType}
value={repositoryType ? repositoryType.name : ""}
options={createSelectOptions()}
helpText={t("help.typeHelpText")}
/>
);
};
export default ImportRepositoryTypeSelect;

View File

@@ -0,0 +1,58 @@
/*
* 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 { RepositoryType, Link } from "@scm-manager/ui-types";
import { Radio } from "@scm-manager/ui-components";
type Props = {
repositoryType: RepositoryType;
importType: string;
setImportType: (type: string) => void;
};
const ImportTypeSelect: FC<Props> = ({repositoryType, importType, setImportType}) => {
const changeImportType = (checked: boolean, name?: string) => {
if (name && checked) {
setImportType(name);
}
};
//TODO Add helptext translation
return (
<>
{(repositoryType._links.import as Link[]).map(type => (
<Radio
name={type.name}
checked={importType === type.name}
value={type.name}
label={type.name}
onChange={changeImportType}
/>
))}
</>
);
};
export default ImportTypeSelect;

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, useState } from "react";
import { InputField } from "@scm-manager/ui-components";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
type Props = {
url: string;
};
const Column = styled.div`
padding: 0 0.75rem;
`;
const Columns = styled.div`
padding: 0.75rem 0 0;
`;
const RepositoryImportFromUrl: FC<Props> = ({}) => {
const [name, setName] = useState("");
const [importUrl, setImportUrl] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [t] = useTranslation("repos");
const handleImportUrlChange = (url: string) => {
if (!name) {
// If the repository name is not fill we set a name suggestion
const match = url.match(/([^\/]+)(\.git)?/i);
if (match && match[1]) {
setName(match[1]);
}
}
setImportUrl(url);
};
return (
<>
<Columns className="columns is-multiline">
<Column className="column is-full">
<InputField
label={t("import.importUrl")}
onChange={handleImportUrlChange}
value={importUrl}
helpText={t("help.importUrlHelpText")}
/>
</Column>
<Column className="column is-half">
<InputField
label={t("import.username")}
onChange={setUsername}
value={username}
helpText={t("help.usernameHelpText")}
/>
</Column>
<Column className="column is-half">
<InputField
label={t("import.password")}
onChange={setPassword}
value={password}
type="password"
helpText={t("help.passwordHelpText")}
/>
</Column>
</Columns>
<hr />
</>
);
};
export default RepositoryImportFromUrl;

View File

@@ -44,13 +44,6 @@ const SpaceBetween = styled.div`
justify-content: space-between;
`;
const Column = styled.div`
padding: 0 0.75rem;
`;
const Columns = styled.div`
padding: 0.75rem 0 0;
`;
type Props = {
createRepository?: (repo: RepositoryCreation, shouldInit: boolean) => void;
@@ -163,48 +156,6 @@ const RepositoryForm: FC<Props> = ({
return <ExtensionPoint name="repos.create.namespace" props={props} renderAll={false} />;
};
const renderUrlImportFields = () => {
if (!isImportMode()) {
return null;
}
return (
<>
<Columns className="columns is-multiline">
<Column className="column is-full">
<InputField
label={t("import.importUrl")}
onChange={handleImportUrlChange}
value={importUrl}
helpText={t("help.importUrlHelpText")}
disabled={disabled}
/>
</Column>
<Column className="column is-half">
<InputField
label={t("import.username")}
onChange={setUsername}
value={username}
helpText={t("help.usernameHelpText")}
disabled={disabled}
/>
</Column>
<Column className="column is-half">
<InputField
label={t("import.password")}
onChange={setPassword}
value={password}
type="password"
helpText={t("help.passwordHelpText")}
disabled={disabled}
/>
</Column>
</Columns>
<hr />
</>
);
};
const renderCreateOnlyFields = () => {
if (isEditMode()) {
return null;
@@ -216,7 +167,6 @@ const RepositoryForm: FC<Props> = ({
};
return (
<>
{renderUrlImportFields()}
{renderNamespaceField()}
<InputField
label={t("repository.name")}
@@ -283,17 +233,6 @@ const RepositoryForm: FC<Props> = ({
setRepo({ ...repo, contact });
};
const handleImportUrlChange = (url: string) => {
if (!repo.name) {
// If the repository name is not fill we set a name suggestion
const match = url.match(/([^\/]+)\.git/i);
if (match && match[1]) {
handleNameChange(match[1]);
}
}
setImportUrl(url);
};
const submitButton = () => {
if (disabled) {
return null;

View File

@@ -60,6 +60,7 @@ import {
} from "../../admin/modules/namespaceStrategies";
import { RouteComponentProps, withRouter } from "react-router-dom";
import { compose } from "redux";
import ImportRepository from "./ImportRepository";
type Props = WithTranslation &
RouteComponentProps & {
@@ -152,7 +153,7 @@ class AddRepository extends React.Component<Props> {
>
<>
{/*//TODO fix this CSS*/}
{!error && <RepositoryFormSwitcher creationMode={this.isImportPage() ? "IMPORT" : "CREATE"} />}
{!error && !importLoading && <RepositoryFormSwitcher creationMode={this.isImportPage() ? "IMPORT" : "CREATE"} />}
{importLoading && (
<>
<Notification type="info">{t("import.pending.infoText")}</Notification>
@@ -160,6 +161,7 @@ class AddRepository extends React.Component<Props> {
<hr/>
</>
)}
<ImportRepository repositoryTypes={repositoryTypes}/>
<RepositoryForm
repositoryTypes={repositoryTypes}
loading={createLoading || importLoading}
@@ -168,6 +170,7 @@ class AddRepository extends React.Component<Props> {
createRepo(repoLink, repo, initRepository, (repo: Repository) => this.repoCreated(repo));
}}
importRepository={repo => {
//TODO Reset error if import started again
importRepoFromUrl(repoLink, repo, (repo: Repository) => this.repoCreated(repo));
}}
indexResources={indexResources}

View File

@@ -0,0 +1,77 @@
/*
* 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, useState } from "react";
import { RepositoryType, Link } from "@scm-manager/ui-types";
import { useTranslation } from "react-i18next";
import ImportRepositoryTypeSelect from "../components/ImportRepositoryTypeSelect";
import ImportTypeSelect from "../components/ImportTypeSelect";
import RepositoryImportFromUrl from "../components/RepositoryImportFromUrl";
type Props = {
repositoryTypes: RepositoryType[];
};
const ImportRepository: FC<Props> = ({ repositoryTypes }) => {
const [repositoryType, setRepositoryType] = useState<RepositoryType | undefined>();
const [importType, setImportType] = useState("");
const [t] = useTranslation("repos");
const changeRepositoryType = (repositoryType: RepositoryType) => {
setImportType(((repositoryType!._links.import as Link[])[0] as Link).name!);
setRepositoryType(repositoryType);
};
const renderImportComponent = () => {
if (importType === "url") {
return (
<RepositoryImportFromUrl
url={((repositoryType!._links.import as Link[])!.find((link: Link) => link.name === "url") as Link).href}
/>
);
}
throw new Error("Unknown import type");
};
return (
<div>
<ImportRepositoryTypeSelect
repositoryTypes={repositoryTypes}
repositoryType={repositoryType}
setRepositoryType={changeRepositoryType}
/>
{repositoryType && (
<>
<hr />
<ImportTypeSelect repositoryType={repositoryType} importType={importType} setImportType={setImportType} />
</>
)}
{importType && renderImportComponent()}
</div>
);
};
export default ImportRepository;