mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-09 15:05:44 +01:00
refactor frontend components
This commit is contained in:
@@ -22,11 +22,12 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Collection } from "./hal";
|
import {Collection, Links} from "./hal";
|
||||||
|
|
||||||
export type RepositoryType = {
|
export type RepositoryType = {
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
|
_links: Links;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RepositoryTypeCollection = Collection & {
|
export type RepositoryTypeCollection = Collection & {
|
||||||
|
|||||||
@@ -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;
|
||||||
58
scm-ui/ui-webapp/src/repos/components/ImportTypeSelect.tsx
Normal file
58
scm-ui/ui-webapp/src/repos/components/ImportTypeSelect.tsx
Normal 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;
|
||||||
@@ -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;
|
||||||
@@ -44,13 +44,6 @@ const SpaceBetween = styled.div`
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Column = styled.div`
|
|
||||||
padding: 0 0.75rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Columns = styled.div`
|
|
||||||
padding: 0.75rem 0 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
createRepository?: (repo: RepositoryCreation, shouldInit: boolean) => void;
|
createRepository?: (repo: RepositoryCreation, shouldInit: boolean) => void;
|
||||||
@@ -163,48 +156,6 @@ const RepositoryForm: FC<Props> = ({
|
|||||||
return <ExtensionPoint name="repos.create.namespace" props={props} renderAll={false} />;
|
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 = () => {
|
const renderCreateOnlyFields = () => {
|
||||||
if (isEditMode()) {
|
if (isEditMode()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -216,7 +167,6 @@ const RepositoryForm: FC<Props> = ({
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{renderUrlImportFields()}
|
|
||||||
{renderNamespaceField()}
|
{renderNamespaceField()}
|
||||||
<InputField
|
<InputField
|
||||||
label={t("repository.name")}
|
label={t("repository.name")}
|
||||||
@@ -283,17 +233,6 @@ const RepositoryForm: FC<Props> = ({
|
|||||||
setRepo({ ...repo, contact });
|
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 = () => {
|
const submitButton = () => {
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import {
|
|||||||
} from "../../admin/modules/namespaceStrategies";
|
} from "../../admin/modules/namespaceStrategies";
|
||||||
import { RouteComponentProps, withRouter } from "react-router-dom";
|
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||||
import { compose } from "redux";
|
import { compose } from "redux";
|
||||||
|
import ImportRepository from "./ImportRepository";
|
||||||
|
|
||||||
type Props = WithTranslation &
|
type Props = WithTranslation &
|
||||||
RouteComponentProps & {
|
RouteComponentProps & {
|
||||||
@@ -152,7 +153,7 @@ class AddRepository extends React.Component<Props> {
|
|||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{/*//TODO fix this CSS*/}
|
{/*//TODO fix this CSS*/}
|
||||||
{!error && <RepositoryFormSwitcher creationMode={this.isImportPage() ? "IMPORT" : "CREATE"} />}
|
{!error && !importLoading && <RepositoryFormSwitcher creationMode={this.isImportPage() ? "IMPORT" : "CREATE"} />}
|
||||||
{importLoading && (
|
{importLoading && (
|
||||||
<>
|
<>
|
||||||
<Notification type="info">{t("import.pending.infoText")}</Notification>
|
<Notification type="info">{t("import.pending.infoText")}</Notification>
|
||||||
@@ -160,6 +161,7 @@ class AddRepository extends React.Component<Props> {
|
|||||||
<hr/>
|
<hr/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<ImportRepository repositoryTypes={repositoryTypes}/>
|
||||||
<RepositoryForm
|
<RepositoryForm
|
||||||
repositoryTypes={repositoryTypes}
|
repositoryTypes={repositoryTypes}
|
||||||
loading={createLoading || importLoading}
|
loading={createLoading || importLoading}
|
||||||
@@ -168,6 +170,7 @@ class AddRepository extends React.Component<Props> {
|
|||||||
createRepo(repoLink, repo, initRepository, (repo: Repository) => this.repoCreated(repo));
|
createRepo(repoLink, repo, initRepository, (repo: Repository) => this.repoCreated(repo));
|
||||||
}}
|
}}
|
||||||
importRepository={repo => {
|
importRepository={repo => {
|
||||||
|
//TODO Reset error if import started again
|
||||||
importRepoFromUrl(repoLink, repo, (repo: Repository) => this.repoCreated(repo));
|
importRepoFromUrl(repoLink, repo, (repo: Repository) => this.repoCreated(repo));
|
||||||
}}
|
}}
|
||||||
indexResources={indexResources}
|
indexResources={indexResources}
|
||||||
|
|||||||
77
scm-ui/ui-webapp/src/repos/containers/ImportRepository.tsx
Normal file
77
scm-ui/ui-webapp/src/repos/containers/ImportRepository.tsx
Normal 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;
|
||||||
Reference in New Issue
Block a user