mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-17 02:31:14 +01:00
Merged in feature/ui-error-handling (pull request #203)
Feature/ui error handling
This commit is contained in:
@@ -14,6 +14,7 @@ type Props = {
|
||||
config?: Config,
|
||||
loading?: boolean,
|
||||
t: string => string,
|
||||
configReadPermission: boolean,
|
||||
configUpdatePermission: boolean
|
||||
};
|
||||
|
||||
@@ -84,16 +85,30 @@ class ConfigForm extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading, t, configUpdatePermission } = this.props;
|
||||
const {
|
||||
loading,
|
||||
t,
|
||||
configReadPermission,
|
||||
configUpdatePermission
|
||||
} = this.props;
|
||||
const config = this.state.config;
|
||||
|
||||
let noPermissionNotification = null;
|
||||
|
||||
if (!configReadPermission) {
|
||||
return (
|
||||
<Notification
|
||||
type={"danger"}
|
||||
children={t("config-form.no-read-permission-notification")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.showNotification) {
|
||||
noPermissionNotification = (
|
||||
<Notification
|
||||
type={"info"}
|
||||
children={t("config-form.no-permission-notification")}
|
||||
children={t("config-form.no-write-permission-notification")}
|
||||
onClose={() => this.onClose()}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { Title, ErrorPage, Loading } from "@scm-manager/ui-components";
|
||||
import { Title, Loading, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import {
|
||||
fetchConfig,
|
||||
getFetchConfigFailure,
|
||||
@@ -35,6 +35,7 @@ type Props = {
|
||||
};
|
||||
|
||||
type State = {
|
||||
configReadPermission: boolean,
|
||||
configChanged: boolean
|
||||
};
|
||||
|
||||
@@ -43,13 +44,18 @@ class GlobalConfig extends React.Component<Props, State> {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
configReadPermission: true,
|
||||
configChanged: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.configReset();
|
||||
this.props.fetchConfig(this.props.configLink);
|
||||
if (this.props.configLink) {
|
||||
this.props.fetchConfig(this.props.configLink);
|
||||
} else {
|
||||
this.setState({configReadPermission: false});
|
||||
}
|
||||
}
|
||||
|
||||
modifyConfig = (config: Config) => {
|
||||
@@ -73,18 +79,8 @@ class GlobalConfig extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t, error, loading, config, configUpdatePermission } = this.props;
|
||||
const { t, loading } = this.props;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage
|
||||
title={t("config.errorTitle")}
|
||||
subtitle={t("config.errorSubtitle")}
|
||||
error={error}
|
||||
configUpdatePermission={configUpdatePermission}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
@@ -92,16 +88,39 @@ class GlobalConfig extends React.Component<Props, State> {
|
||||
return (
|
||||
<div>
|
||||
<Title title={t("config.title")} />
|
||||
{this.renderConfigChangedNotification()}
|
||||
<ConfigForm
|
||||
submitForm={config => this.modifyConfig(config)}
|
||||
config={config}
|
||||
loading={loading}
|
||||
configUpdatePermission={configUpdatePermission}
|
||||
/>
|
||||
{this.renderError()}
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderError = () => {
|
||||
const { error } = this.props;
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
renderContent = () => {
|
||||
const { error, loading, config, configUpdatePermission } = this.props;
|
||||
const { configReadPermission } = this.state;
|
||||
if (!error) {
|
||||
return (
|
||||
<>
|
||||
{this.renderConfigChangedNotification()}
|
||||
<ConfigForm
|
||||
submitForm={config => this.modifyConfig(config)}
|
||||
config={config}
|
||||
loading={loading}
|
||||
configUpdatePermission={configUpdatePermission}
|
||||
configReadPermission={configReadPermission}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
|
||||
@@ -15,8 +15,7 @@ import {
|
||||
InputField,
|
||||
SubmitButton,
|
||||
ErrorNotification,
|
||||
Image,
|
||||
UNAUTHORIZED_ERROR
|
||||
Image, UnauthorizedError
|
||||
} from "@scm-manager/ui-components";
|
||||
import classNames from "classnames";
|
||||
import { getLoginLink } from "../modules/indexResource";
|
||||
@@ -95,7 +94,7 @@ class Login extends React.Component<Props, State> {
|
||||
|
||||
areCredentialsInvalid() {
|
||||
const { t, error } = this.props;
|
||||
if (error === UNAUTHORIZED_ERROR) {
|
||||
if (error instanceof UnauthorizedError) {
|
||||
return new Error(t("error-notification.wrong-login-credentials"));
|
||||
} else {
|
||||
return error;
|
||||
|
||||
@@ -54,8 +54,8 @@ export function fetchGroupsByLink(link: string) {
|
||||
.then(data => {
|
||||
dispatch(fetchGroupsSuccess(data));
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(fetchGroupsFailure(link, err));
|
||||
.catch(error => {
|
||||
dispatch(fetchGroupsFailure(link, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -104,8 +104,8 @@ function fetchGroup(link: string, name: string) {
|
||||
.then(data => {
|
||||
dispatch(fetchGroupSuccess(data));
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(fetchGroupFailure(name, err));
|
||||
.catch(error => {
|
||||
dispatch(fetchGroupFailure(name, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -149,12 +149,8 @@ export function createGroup(link: string, group: Group, callback?: () => void) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(
|
||||
createGroupFailure(
|
||||
err
|
||||
)
|
||||
);
|
||||
.catch(error => {
|
||||
dispatch(createGroupFailure(error));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -199,13 +195,8 @@ export function modifyGroup(group: Group, callback?: () => void) {
|
||||
.then(() => {
|
||||
dispatch(fetchGroupByLink(group));
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(
|
||||
modifyGroupFailure(
|
||||
group,
|
||||
err
|
||||
)
|
||||
);
|
||||
.catch(error => {
|
||||
dispatch(modifyGroupFailure(group, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -257,8 +248,8 @@ export function deleteGroup(group: Group, callback?: () => void) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(deleteGroupFailure(group, err));
|
||||
.catch(error => {
|
||||
dispatch(deleteGroupFailure(group, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -342,7 +333,7 @@ function listReducer(state: any = {}, action: any = {}) {
|
||||
...state,
|
||||
entries: groupNames,
|
||||
entry: {
|
||||
groupCreatePermission: action.payload._links.create ? true : false,
|
||||
groupCreatePermission: !!action.payload._links.create,
|
||||
page: action.payload.page,
|
||||
pageTotal: action.payload.pageTotal,
|
||||
_links: action.payload._links
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { Me } from "@scm-manager/ui-types";
|
||||
import * as types from "./types";
|
||||
|
||||
import { apiClient, UNAUTHORIZED_ERROR } from "@scm-manager/ui-components";
|
||||
import { apiClient, UnauthorizedError } from "@scm-manager/ui-components";
|
||||
import { isPending } from "./pending";
|
||||
import { getFailure } from "./failure";
|
||||
import {
|
||||
@@ -152,7 +152,7 @@ export const login = (
|
||||
dispatch(loginPending());
|
||||
return apiClient
|
||||
.post(loginLink, login_data)
|
||||
.then(response => {
|
||||
.then(() => {
|
||||
dispatch(fetchIndexResourcesPending());
|
||||
return callFetchIndexResources();
|
||||
})
|
||||
@@ -178,7 +178,7 @@ export const fetchMe = (link: string) => {
|
||||
dispatch(fetchMeSuccess(me));
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
if (error === UNAUTHORIZED_ERROR) {
|
||||
if (error instanceof UnauthorizedError) {
|
||||
dispatch(fetchMeUnauthenticated());
|
||||
} else {
|
||||
dispatch(fetchMeFailure(error));
|
||||
|
||||
@@ -179,9 +179,7 @@ describe("auth actions", () => {
|
||||
});
|
||||
|
||||
it("should dispatch fetch me unauthorized", () => {
|
||||
fetchMock.getOnce("/api/v2/me", {
|
||||
status: 401
|
||||
});
|
||||
fetchMock.getOnce("/api/v2/me", 401);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_ME_PENDING },
|
||||
|
||||
@@ -12,13 +12,13 @@ import { Route, Switch } from "react-router-dom";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
|
||||
import {
|
||||
ErrorPage,
|
||||
CollapsibleErrorPage,
|
||||
Loading,
|
||||
Navigation,
|
||||
SubNavigation,
|
||||
NavLink,
|
||||
Page,
|
||||
Section
|
||||
Section, ErrorPage
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import RepositoryDetails from "../components/RepositoryDetails";
|
||||
@@ -82,13 +82,11 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
const { loading, error, indexLinks, repository, t } = this.props;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage
|
||||
title={t("repositoryRoot.errorTitle")}
|
||||
subtitle={t("repositoryRoot.errorSubtitle")}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
return <ErrorPage
|
||||
title={t("repositoryRoot.errorTitle")}
|
||||
subtitle={t("repositoryRoot.errorSubtitle")}
|
||||
error={error}
|
||||
/>
|
||||
}
|
||||
|
||||
if (!repository || loading) {
|
||||
|
||||
@@ -229,8 +229,8 @@ export function modifyRepo(repository: Repository, callback?: () => void) {
|
||||
.then(() => {
|
||||
dispatch(fetchRepoByLink(repository));
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(modifyRepoFailure(repository, err));
|
||||
.catch(error => {
|
||||
dispatch(modifyRepoFailure(repository, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,10 +37,7 @@ function fetchRepositoryTypes(dispatch: any) {
|
||||
.then(repositoryTypes => {
|
||||
dispatch(fetchRepositoryTypesSuccess(repositoryTypes));
|
||||
})
|
||||
.catch(err => {
|
||||
const error = new Error(
|
||||
`failed to fetch repository types: ${err.message}`
|
||||
);
|
||||
.catch(error => {
|
||||
dispatch(fetchRepositoryTypesFailure(error));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ export const DELETE_USER_FAILURE = `${DELETE_USER}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
const CONTENT_TYPE_USER = "application/vnd.scmm-user+json;v=2";
|
||||
|
||||
// TODO i18n for error messages
|
||||
|
||||
// fetch users
|
||||
|
||||
export function fetchUsers(link: string) {
|
||||
@@ -55,8 +57,8 @@ export function fetchUsersByLink(link: string) {
|
||||
.then(data => {
|
||||
dispatch(fetchUsersSuccess(data));
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(fetchUsersFailure(link, err));
|
||||
.catch(error => {
|
||||
dispatch(fetchUsersFailure(link, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -105,8 +107,8 @@ function fetchUser(link: string, name: string) {
|
||||
.then(data => {
|
||||
dispatch(fetchUserSuccess(data));
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(fetchUserFailure(name, err));
|
||||
.catch(error => {
|
||||
dispatch(fetchUserFailure(name, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -151,7 +153,9 @@ export function createUser(link: string, user: User, callback?: () => void) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
.catch(err => dispatch(createUserFailure(err)));
|
||||
.catch(error =>
|
||||
dispatch(createUserFailure(error))
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -250,8 +254,8 @@ export function deleteUser(user: User, callback?: () => void) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(deleteUserFailure(user, err));
|
||||
.catch(error => {
|
||||
dispatch(deleteUserFailure(user, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user