mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-09 06:55:47 +01:00
fix form validation after refactoring
This commit is contained in:
@@ -61,6 +61,7 @@ const MarginLeft = styled.div`
|
||||
const FlexContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 2.25rem;
|
||||
`;
|
||||
|
||||
export default class Page extends React.Component<Props> {
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
"infoText": "Ihr Repository wird gerade importiert. Dies kann einen Moment dauern. Sie werden weitergeleitet, sobald der Import abgeschlossen ist. Wenn Sie diese Seite verlassen, können Sie nicht zurückkehren, um den Import-Status zu erfahren."
|
||||
},
|
||||
"importTypes": {
|
||||
"label": "Import Modus",
|
||||
"url": {
|
||||
"label": "Import via URL",
|
||||
"helpText": "Das Repository wird über eine URL importiert."
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"infoText": "Your repository is currently being imported. This may take a moment. You will be forwarded as soon as the import is finished. If you leave this page you cannot return to find out the import status."
|
||||
},
|
||||
"importTypes": {
|
||||
"label": "Import Mode",
|
||||
"url": {
|
||||
"label": "Import via URL",
|
||||
"helpText": "The Repository will be imported via the provided URL."
|
||||
|
||||
@@ -32,6 +32,7 @@ type Props = {
|
||||
repository: RepositoryUrlImport;
|
||||
onChange: (repository: RepositoryUrlImport) => void;
|
||||
setValid: (valid: boolean) => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const Column = styled.div`
|
||||
@@ -42,7 +43,7 @@ const Columns = styled.div`
|
||||
padding: 0.75rem 0 0;
|
||||
`;
|
||||
|
||||
const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
|
||||
const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid, disabled }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const [urlValidationError, setUrlValidationError] = useState(false);
|
||||
|
||||
@@ -72,6 +73,7 @@ const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
|
||||
helpText={t("help.importUrlHelpText")}
|
||||
validationError={urlValidationError}
|
||||
errorMessage={t("validation.url-invalid")}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Column>
|
||||
<Column className="column is-half">
|
||||
@@ -80,6 +82,7 @@ const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
|
||||
onChange={username => onChange({ ...repository, username })}
|
||||
value={repository.username}
|
||||
helpText={t("help.usernameHelpText")}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Column>
|
||||
<Column className="column is-half">
|
||||
@@ -89,6 +92,7 @@ const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
|
||||
value={repository.password}
|
||||
type="password"
|
||||
helpText={t("help.passwordHelpText")}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Column>
|
||||
</Columns>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC, useState } from "react";
|
||||
import React, { FC, FormEvent, useState } from "react";
|
||||
import NamespaceAndNameFields from "./NamespaceAndNameFields";
|
||||
import { Repository, RepositoryUrlImport } from "@scm-manager/ui-types";
|
||||
import ImportFromUrlForm from "./ImportFromUrlForm";
|
||||
@@ -62,7 +62,9 @@ const ImportRepositoryFromUrl: FC<Props> = ({ url, setImportPending }) => {
|
||||
setImportPending(loading);
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
const submit = (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const currentPath = history.location.pathname;
|
||||
handleImportLoading(true);
|
||||
apiClient
|
||||
.post(url, repo, "application/vnd.scmm-repository+json;v=2")
|
||||
@@ -72,7 +74,11 @@ const ImportRepositoryFromUrl: FC<Props> = ({ url, setImportPending }) => {
|
||||
return apiClient.get(location);
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(repo => history.push(`/repo/${repo.namespace}/${repo.name}/code/sources`))
|
||||
.then(repo => {
|
||||
if (history.location.pathname === currentPath) {
|
||||
history.push(`/repo/${repo.namespace}/${repo.name}/code/sources`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
setError(error);
|
||||
handleImportLoading(false);
|
||||
@@ -81,22 +87,24 @@ const ImportRepositoryFromUrl: FC<Props> = ({ url, setImportPending }) => {
|
||||
|
||||
return (
|
||||
<form onSubmit={submit}>
|
||||
<ErrorNotification error={error} />
|
||||
<ImportFromUrlForm
|
||||
repository={repo}
|
||||
onChange={setRepo}
|
||||
setValid={(importUrl: boolean) => setValid({ ...valid, importUrl })}
|
||||
disabled={loading}
|
||||
/>
|
||||
<ErrorNotification error={error} />
|
||||
<hr />
|
||||
<NamespaceAndNameFields
|
||||
repository={repo}
|
||||
onChange={setRepo as React.Dispatch<React.SetStateAction<Repository>>}
|
||||
setValid={(namespaceAndName: boolean) => setValid({ ...valid, namespaceAndName })}
|
||||
disabled={loading}
|
||||
/>
|
||||
<RepositoryInformationForm
|
||||
repository={repo}
|
||||
onChange={setRepo as React.Dispatch<React.SetStateAction<Repository>>}
|
||||
disabled={false}
|
||||
disabled={loading}
|
||||
setValid={(contact: boolean) => setValid({ ...valid, contact })}
|
||||
/>
|
||||
<Level
|
||||
|
||||
@@ -30,13 +30,16 @@ type Props = {
|
||||
repositoryTypes: RepositoryType[];
|
||||
repositoryType?: RepositoryType;
|
||||
setRepositoryType: (repositoryType: RepositoryType) => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const ImportRepositoryTypeSelect: FC<Props> = ({ repositoryTypes, repositoryType, setRepositoryType }) => {
|
||||
const ImportRepositoryTypeSelect: FC<Props> = ({ repositoryTypes, repositoryType, setRepositoryType, disabled }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
|
||||
const createSelectOptions = () => {
|
||||
const options = repositoryTypes.filter(repoType => !!repoType._links.import).map(repositoryType => {
|
||||
const options = repositoryTypes
|
||||
.filter(repoType => !!repoType._links.import)
|
||||
.map(repositoryType => {
|
||||
return {
|
||||
label: repositoryType.displayName,
|
||||
value: repositoryType.name
|
||||
@@ -58,6 +61,7 @@ const ImportRepositoryTypeSelect: FC<Props> = ({ repositoryTypes, repositoryType
|
||||
value={repositoryType ? repositoryType.name : ""}
|
||||
options={createSelectOptions()}
|
||||
helpText={t("help.typeHelpText")}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -22,17 +22,18 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import { RepositoryType, Link } from "@scm-manager/ui-types";
|
||||
import { Radio } from "@scm-manager/ui-components";
|
||||
import { Link, RepositoryType } from "@scm-manager/ui-types";
|
||||
import { LabelWithHelpIcon, Radio } from "@scm-manager/ui-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
repositoryType: RepositoryType;
|
||||
importType: string;
|
||||
setImportType: (type: string) => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType }) => {
|
||||
const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType, disabled }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
|
||||
const changeImportType = (checked: boolean, name?: string) => {
|
||||
@@ -43,6 +44,7 @@ const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType
|
||||
|
||||
return (
|
||||
<>
|
||||
<LabelWithHelpIcon label={t("import.importTypes.label")} key="import.importTypes.label" />
|
||||
{(repositoryType._links.import as Link[]).map((type, index) => (
|
||||
<Radio
|
||||
name={type.name}
|
||||
@@ -52,6 +54,7 @@ const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType
|
||||
helpText={t(`import.importTypes.${type.name}.helpText`)}
|
||||
onChange={changeImportType}
|
||||
key={index}
|
||||
disabled={disabled}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -36,45 +36,45 @@ type Props = {
|
||||
onChange: (repository: Repository) => void;
|
||||
namespaceStrategy: string;
|
||||
setValid: (valid: boolean) => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStrategy, setValid }) => {
|
||||
const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStrategy, setValid, disabled }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const [namespaceValidationError, setNamespaceValidationError] = useState(false);
|
||||
const [nameValidationError, setNameValidationError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
//TODO fix validation
|
||||
if (repository.name) {
|
||||
const valid = validator.isNameValid(repository.name);
|
||||
setNameValidationError(!valid);
|
||||
onFieldChange();
|
||||
const nameValid = validator.isNameValid(repository.name);
|
||||
setNameValidationError(!nameValid);
|
||||
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY) {
|
||||
if (repository.namespace) {
|
||||
const namespaceValid = validator.isNamespaceValid(repository.namespace);
|
||||
setValid(!(!namespaceValid || !nameValid));
|
||||
} else {
|
||||
setValid(false);
|
||||
}
|
||||
}, [repository.name]);
|
||||
} else {
|
||||
setValid(nameValid);
|
||||
}
|
||||
} else {
|
||||
setValid(false);
|
||||
}
|
||||
}, [repository.name, repository.namespace]);
|
||||
|
||||
const handleNamespaceChange = (namespace: string) => {
|
||||
const valid = validator.isNamespaceValid(namespace);
|
||||
setNamespaceValidationError(!valid);
|
||||
onFieldChange(valid);
|
||||
onChange({ ...repository, namespace });
|
||||
};
|
||||
|
||||
const handleNameChange = (name: string) => {
|
||||
const valid = validator.isNameValid(name);
|
||||
setNameValidationError(!valid);
|
||||
onFieldChange();
|
||||
onChange({ ...repository, name });
|
||||
};
|
||||
|
||||
//TODO fix validation
|
||||
const onFieldChange = (namespaceValid?: boolean) => {
|
||||
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY && !validator.isNamespaceValid(repository.namespace)) {
|
||||
setValid(false);
|
||||
} else {
|
||||
setValid(!nameValidationError && !namespaceValidationError);
|
||||
}
|
||||
};
|
||||
|
||||
const renderNamespaceField = () => {
|
||||
const props = {
|
||||
label: t("repository.namespace"),
|
||||
@@ -82,7 +82,8 @@ const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStra
|
||||
value: repository ? repository.namespace : "",
|
||||
onChange: handleNamespaceChange,
|
||||
errorMessage: t("validation.namespace-invalid"),
|
||||
validationError: namespaceValidationError
|
||||
validationError: namespaceValidationError,
|
||||
disabled: disabled
|
||||
};
|
||||
|
||||
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY) {
|
||||
@@ -102,6 +103,7 @@ const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStra
|
||||
validationError={nameValidationError}
|
||||
errorMessage={t("validation.name-invalid")}
|
||||
helpText={t("help.nameHelpText")}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -78,7 +78,6 @@ const RepositoryForm: FC<Props> = ({
|
||||
});
|
||||
const [initRepository, setInitRepository] = useState(false);
|
||||
const [contextEntries, setContextEntries] = useState({});
|
||||
//TODO fix validation
|
||||
const [valid, setValid] = useState({ namespaceAndName: false, contact: true });
|
||||
const [t] = useTranslation("repos");
|
||||
|
||||
@@ -194,7 +193,7 @@ const RepositoryForm: FC<Props> = ({
|
||||
<RepositoryInformationForm
|
||||
repository={repo}
|
||||
onChange={setRepo}
|
||||
disabled={!createRepository}
|
||||
disabled={!(isModifiable() || createRepository)}
|
||||
setValid={contact => setValid({ ...valid, contact })}
|
||||
/>
|
||||
{submitButton()}
|
||||
|
||||
@@ -25,12 +25,16 @@
|
||||
import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button, ButtonAddons, Level } from "@scm-manager/ui-components";
|
||||
import { Button, ButtonAddons, Icon, Level } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
creationMode: "CREATE" | "IMPORT";
|
||||
};
|
||||
|
||||
const MarginIcon = styled(Icon)`
|
||||
padding-right: 0.5rem;
|
||||
`;
|
||||
|
||||
const SmallButton = styled(Button)`
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
@@ -38,9 +42,14 @@ const SmallButton = styled(Button)`
|
||||
`;
|
||||
|
||||
const TopLevel = styled(Level)`
|
||||
margin-top: -2.75rem;
|
||||
margin-bottom: 2.75rem !important; //TODO Try to remove important
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: -1.5rem !important;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
@media (max-width: 785px) {
|
||||
margin-top: 4.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const RepositoryFormSwitcher: FC<Props> = ({ creationMode }) => {
|
||||
@@ -59,17 +68,20 @@ const RepositoryFormSwitcher: FC<Props> = ({ creationMode }) => {
|
||||
right={
|
||||
<ButtonAddons>
|
||||
<SmallButton
|
||||
label={t("repositoryForm.createButton")}
|
||||
icon="fa fa-plus"
|
||||
color={isCreateMode() ? "link is-selected" : undefined}
|
||||
link={isImportMode() ? "/repos/create" : undefined}
|
||||
/>
|
||||
>
|
||||
<MarginIcon name="fa fa-plus" color={isCreateMode() ? "white" : "default"} />{" "}
|
||||
<p className="is-hidden-mobile is-hidden-tablet-only">{t("repositoryForm.createButton")}</p>
|
||||
</SmallButton>
|
||||
<SmallButton
|
||||
label={t("repositoryForm.importButton")}
|
||||
icon="fa fa-file-upload"
|
||||
color={isImportMode() ? "link is-selected" : undefined}
|
||||
link={isCreateMode() ? "/repos/import" : undefined}
|
||||
/>
|
||||
className="has-text-left-desktop"
|
||||
>
|
||||
<MarginIcon name="fa fa-file-upload" color={isImportMode() ? "white" : "default"} />
|
||||
<p className="is-hidden-mobile is-hidden-tablet-only">{t("repositoryForm.importButton")}</p>
|
||||
</SmallButton>
|
||||
</ButtonAddons>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -35,12 +35,7 @@ import {
|
||||
} from "../modules/repositoryTypes";
|
||||
import RepositoryForm from "../components/form";
|
||||
import RepositoryFormSwitcher from "../components/form/RepositoryFormSwitcher";
|
||||
import {
|
||||
createRepo,
|
||||
createRepoReset,
|
||||
getCreateRepoFailure,
|
||||
isCreateRepoPending
|
||||
} from "../modules/repos";
|
||||
import { createRepo, createRepoReset, getCreateRepoFailure, isCreateRepoPending } from "../modules/repos";
|
||||
import { getRepositoriesLink } from "../../modules/indexResource";
|
||||
import {
|
||||
fetchNamespaceStrategiesIfNeeded,
|
||||
@@ -77,7 +72,19 @@ type Props = WithTranslation &
|
||||
history: History;
|
||||
};
|
||||
|
||||
class AddRepository extends React.Component<Props> {
|
||||
type State = {
|
||||
importPending: boolean;
|
||||
};
|
||||
|
||||
class AddRepository extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
importPending: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.resetForm();
|
||||
this.props.fetchRepositoryTypesIfNeeded();
|
||||
@@ -102,6 +109,16 @@ class AddRepository extends React.Component<Props> {
|
||||
isImportPage = () => this.resolveLocation() === "import";
|
||||
isCreatePage = () => this.resolveLocation() === "create";
|
||||
|
||||
getSubtitle = () => {
|
||||
if (this.isCreatePage()) {
|
||||
return "create.subtitle";
|
||||
} else if (this.isImportPage() && this.state.importPending) {
|
||||
return "import.pending.subtitle";
|
||||
} else {
|
||||
return "import.subtitle";
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
pageLoading,
|
||||
@@ -118,13 +135,18 @@ class AddRepository extends React.Component<Props> {
|
||||
return (
|
||||
<Page
|
||||
title={t("create.title")}
|
||||
subtitle={t("create.subtitle")}
|
||||
subtitle={t(this.getSubtitle())}
|
||||
afterTitle={<RepositoryFormSwitcher creationMode={this.isImportPage() ? "IMPORT" : "CREATE"} />}
|
||||
loading={pageLoading}
|
||||
error={error}
|
||||
showContentOnError={true}
|
||||
>
|
||||
{!error && <RepositoryFormSwitcher creationMode={this.isImportPage() ? "IMPORT" : "CREATE"} />}
|
||||
{this.isImportPage() && <ImportRepository repositoryTypes={repositoryTypes} />}
|
||||
{this.isImportPage() && (
|
||||
<ImportRepository
|
||||
repositoryTypes={repositoryTypes}
|
||||
setPending={(importPending: boolean) => this.setState({ importPending })}
|
||||
/>
|
||||
)}
|
||||
{this.isCreatePage() && (
|
||||
<RepositoryForm
|
||||
repositoryTypes={repositoryTypes}
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC, useState } from "react";
|
||||
import { RepositoryType, Link } from "@scm-manager/ui-types";
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
import { Link, RepositoryType } from "@scm-manager/ui-types";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ImportRepositoryTypeSelect from "../components/ImportRepositoryTypeSelect";
|
||||
@@ -33,14 +33,19 @@ import { Loading, Notification } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repositoryTypes: RepositoryType[];
|
||||
setPending: (pending: boolean) => void;
|
||||
};
|
||||
|
||||
const ImportRepository: FC<Props> = ({ repositoryTypes }) => {
|
||||
const ImportRepository: FC<Props> = ({ repositoryTypes, setPending }) => {
|
||||
const [importPending, setImportPending] = useState(false);
|
||||
const [repositoryType, setRepositoryType] = useState<RepositoryType | undefined>();
|
||||
const [importType, setImportType] = useState("");
|
||||
const [t] = useTranslation("repos");
|
||||
|
||||
useEffect(() => {
|
||||
setPending(importPending);
|
||||
}, [importPending]);
|
||||
|
||||
const changeRepositoryType = (repositoryType: RepositoryType) => {
|
||||
setRepositoryType(repositoryType);
|
||||
setImportType(repositoryType?._links ? ((repositoryType!._links?.import as Link[])[0] as Link).name! : "");
|
||||
@@ -72,11 +77,17 @@ const ImportRepository: FC<Props> = ({ repositoryTypes }) => {
|
||||
repositoryTypes={repositoryTypes}
|
||||
repositoryType={repositoryType}
|
||||
setRepositoryType={changeRepositoryType}
|
||||
disabled={importPending}
|
||||
/>
|
||||
{repositoryType && (
|
||||
<>
|
||||
<hr />
|
||||
<ImportTypeSelect repositoryType={repositoryType} importType={importType} setImportType={setImportType} />
|
||||
<ImportTypeSelect
|
||||
repositoryType={repositoryType}
|
||||
importType={importType}
|
||||
setImportType={setImportType}
|
||||
disabled={importPending}
|
||||
/>
|
||||
<hr />
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user