Feat/page not found (#37)
* Feat: Implement page not found UI * fix: check user login status and minor code refactoring - Check if user is logged in or not in page header. - Based on login status show relevant CTAs - Fix ESLint errors - Move RoleNameCell and DeleteActionCell as seperate components. * fix: Add catch all for pagenotfound * fix: Use constants and update css syntax. Co-authored-by: Arpit Mohan <me@arpitmohan.com>
This commit is contained in:
parent
5f39dfd88b
commit
7737b57667
|
|
@ -5,7 +5,7 @@ import {
|
|||
API_REQUEST_HEADERS,
|
||||
} from "constants/ApiConstants";
|
||||
import { ActionApiResponse } from "./ActionAPI";
|
||||
import { AUTH_LOGIN_URL } from "constants/routes";
|
||||
import { AUTH_LOGIN_URL, PAGE_NOT_FOUND_URL } from "constants/routes";
|
||||
import { setRouteBeforeLogin } from "utils/storage";
|
||||
import history from "utils/history";
|
||||
|
||||
|
|
@ -66,6 +66,17 @@ axiosInstance.interceptors.response.use(
|
|||
});
|
||||
}
|
||||
}
|
||||
if (
|
||||
error.resonse.status === 404 &&
|
||||
error.response.app_error_code === 4028
|
||||
) {
|
||||
history.push(PAGE_NOT_FOUND_URL);
|
||||
return Promise.reject({
|
||||
code: 404,
|
||||
message: "Page Not Found",
|
||||
show: false,
|
||||
});
|
||||
}
|
||||
if (error.response.data.responseMeta) {
|
||||
return Promise.resolve(error.response.data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ class UserApi extends Api {
|
|||
static switchUserOrgURL = `${UserApi.usersURL}/switchOrganization`;
|
||||
static addOrgURL = `${UserApi.usersURL}/addOrganization`;
|
||||
static logoutURL = "v1/logout";
|
||||
static currentUserURL = "v1/users/me";
|
||||
|
||||
static createUser(
|
||||
request: CreateUserRequest,
|
||||
|
|
@ -76,6 +77,10 @@ class UserApi extends Api {
|
|||
return Api.get(UserApi.usersURL + "/" + request.id);
|
||||
}
|
||||
|
||||
static getCurrentUser(): AxiosPromise<ApiResponse> {
|
||||
return Api.get(UserApi.currentUserURL);
|
||||
}
|
||||
|
||||
static forgotPassword(
|
||||
request: ForgotPasswordRequest,
|
||||
): AxiosPromise<ApiResponse> {
|
||||
|
|
|
|||
BIN
app/client/src/assets/images/404-image.png
Normal file
BIN
app/client/src/assets/images/404-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 141 KiB |
|
|
@ -216,6 +216,7 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
GET_ALL_APPLICATION_INIT: "GET_ALL_APPLICATION_INIT",
|
||||
FETCH_USER_APPLICATIONS_ORGS_SUCCESS: "FETCH_USER_APPLICATIONS_ORGS_SUCCESS",
|
||||
FETCH_USER_DETAILS_SUCCESS: "FETCH_USER_DETAILS_SUCCESS",
|
||||
FETCH_USER_DETAILS_ERROR: "FETCH_USER_DETAILS_ERROR",
|
||||
FETCH_ALL_USERS_SUCCESS: "FETCH_ALL_USERS_SUCCESS",
|
||||
FETCH_ALL_USERS_INIT: "FETCH_ALL_USERS_INIT",
|
||||
FETCH_ALL_ROLES_SUCCESS: "FETCH_ALL_ROLES_SUCCESS",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { MenuIcons } from "icons/MenuIcons";
|
|||
|
||||
export const BASE_URL = "/";
|
||||
export const ORG_URL = "/org";
|
||||
export const PAGE_NOT_FOUND_URL = "/404";
|
||||
export const APPLICATIONS_URL = `/applications`;
|
||||
export const BUILDER_URL = "/applications/:applicationId/pages/:pageId/edit";
|
||||
export const USER_AUTH_URL = "/user";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
export const ANONYMOUS_USERNAME = "anonymousUser";
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
email: string;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
BASE_LOGIN_URL,
|
||||
BASE_SIGNUP_URL,
|
||||
USERS_URL,
|
||||
PAGE_NOT_FOUND_URL,
|
||||
} from "constants/routes";
|
||||
import { LayersContext, Layers } from "constants/Layers";
|
||||
|
||||
|
|
@ -120,6 +121,11 @@ ReactDOM.render(
|
|||
routeProtected
|
||||
logDisable
|
||||
/>
|
||||
<AppRoute
|
||||
path={PAGE_NOT_FOUND_URL}
|
||||
component={PageNotFound}
|
||||
name={"PageNotFound"}
|
||||
/>
|
||||
<AppRoute component={PageNotFound} name={"PageNotFound"} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {
|
|||
theme,
|
||||
getBorderCSSShorthand,
|
||||
getColorWithOpacity,
|
||||
Theme,
|
||||
} from "constants/DefaultTheme";
|
||||
import ContextDropdown, {
|
||||
ContextDropdownOption,
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ import { PERMISSION_TYPE, isPermitted } from "./permissionHelpers";
|
|||
import { MenuIcons } from "icons/MenuIcons";
|
||||
import { DELETING_APPLICATION } from "constants/messages";
|
||||
import { AppToaster } from "components/editorComponents/ToastComponent";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import FormDialogComponent from "components/editorComponents/form/FormDialogComponent";
|
||||
import OrganizationListMockResponse from "mockResponses/OrganisationListResponse";
|
||||
import { User } from "constants/userConstants";
|
||||
import CustomizedDropdown, {
|
||||
CustomizedDropdownProps,
|
||||
|
|
@ -107,7 +105,6 @@ const StyledDialog = styled(Dialog)<{ setMaxWidth?: boolean }>`
|
|||
|
||||
type ApplicationProps = {
|
||||
applicationList: ApplicationPayload[];
|
||||
fetchApplications: () => void;
|
||||
createApplication: (appName: string) => void;
|
||||
isCreatingApplication: boolean;
|
||||
isFetchingApplications: boolean;
|
||||
|
|
@ -319,8 +316,6 @@ const mapStateToProps = (state: AppState) => ({
|
|||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => ({
|
||||
fetchApplications: () =>
|
||||
dispatch({ type: ReduxActionTypes.FETCH_APPLICATION_LIST_INIT }),
|
||||
getAllApplication: () =>
|
||||
dispatch({ type: ReduxActionTypes.GET_ALL_APPLICATION_INIT }),
|
||||
createApplication: (appName: string) => {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
import React from "react";
|
||||
import { useHistory, Link } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import { getCurrentUser } from "selectors/usersSelectors";
|
||||
import { getOrgs, getCurrentOrg } from "selectors/organizationSelectors";
|
||||
import styled from "styled-components";
|
||||
import StyledHeader from "components/designSystems/appsmith/StyledHeader";
|
||||
import CustomizedDropdown from "./CustomizedDropdown";
|
||||
import DropdownProps from "./CustomizedDropdown/HeaderDropdownData";
|
||||
import { AppState } from "reducers";
|
||||
import { Org } from "constants/orgConstants";
|
||||
import { User } from "constants/userConstants";
|
||||
import { User, ANONYMOUS_USERNAME } from "constants/userConstants";
|
||||
import Logo from "assets/images/appsmith_logo.png";
|
||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import { useEffect } from "react";
|
||||
import { AUTH_LOGIN_URL, APPLICATIONS_URL } from "constants/routes";
|
||||
import Button from "components/editorComponents/Button";
|
||||
|
||||
const StyledPageHeader = styled(StyledHeader)`
|
||||
width: 100%;
|
||||
|
|
@ -28,31 +31,47 @@ const LogoContainer = styled.div`
|
|||
`;
|
||||
|
||||
type PageHeaderProps = {
|
||||
orgs?: Org[];
|
||||
currentOrg?: Org;
|
||||
user?: User;
|
||||
fetchCurrentUser: () => void;
|
||||
};
|
||||
|
||||
export const PageHeader = (props: PageHeaderProps) => {
|
||||
const { user } = props;
|
||||
const { user, fetchCurrentUser } = props;
|
||||
const history = useHistory();
|
||||
useEffect(() => {
|
||||
fetchCurrentUser();
|
||||
}, [fetchCurrentUser]);
|
||||
|
||||
return (
|
||||
<StyledPageHeader>
|
||||
<LogoContainer>
|
||||
<a href="/applications">
|
||||
<Link to={APPLICATIONS_URL}>
|
||||
<img className="logoimg" src={Logo} alt="Appsmith Logo" />
|
||||
</a>
|
||||
</Link>
|
||||
</LogoContainer>
|
||||
<StyledDropDownContainer>
|
||||
{user && <CustomizedDropdown {...DropdownProps(user, user.username)} />}
|
||||
{user && user.username !== ANONYMOUS_USERNAME ? (
|
||||
<CustomizedDropdown {...DropdownProps(user, user.username)} />
|
||||
) : (
|
||||
<Button
|
||||
filled
|
||||
text="Sign In"
|
||||
intent={"primary"}
|
||||
size="small"
|
||||
onClick={() => history.push(AUTH_LOGIN_URL)}
|
||||
/>
|
||||
)}
|
||||
</StyledDropDownContainer>
|
||||
</StyledPageHeader>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
currentOrg: getCurrentOrg(state),
|
||||
user: getCurrentUser(state),
|
||||
orgs: getOrgs(state),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null)(PageHeader);
|
||||
const mapDispatchToProps = (dispatch: any) => ({
|
||||
fetchCurrentUser: () => dispatch({ type: ReduxActionTypes.FETCH_USER_INIT }),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PageHeader);
|
||||
|
|
|
|||
|
|
@ -1,45 +1,56 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { NonIdealState, Button, Card, Elevation } from "@blueprintjs/core";
|
||||
import { RouterProps } from "react-router";
|
||||
import styled from "styled-components";
|
||||
import Button from "components/editorComponents/Button";
|
||||
import PageUnavailableImage from "assets/images/404-image.png";
|
||||
import PageHeader from "pages/common/PageHeader";
|
||||
import { BASE_URL } from "constants/routes";
|
||||
|
||||
const NotFoundPageWrapper = styled.div`
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
const Title = styled.div`
|
||||
font-size: ${props => props.theme.fontSizes[10]}px;
|
||||
const Wrapper = styled.div`
|
||||
text-align: center;
|
||||
margin-top: 5%;
|
||||
.bold-text {
|
||||
font-weight: ${props => props.theme.fontWeights[3]};
|
||||
font-size: 24px;
|
||||
}
|
||||
.page-unavailable-img {
|
||||
width: 35%;
|
||||
}
|
||||
.button-position {
|
||||
margin: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
class PageNotFound extends React.PureComponent<RouterProps> {
|
||||
public render() {
|
||||
return (
|
||||
<NotFoundPageWrapper>
|
||||
<Card elevation={Elevation.TWO}>
|
||||
<Title>
|
||||
<span role="img" aria-label="Page Not Found">
|
||||
🙊
|
||||
</span>
|
||||
</Title>
|
||||
<NonIdealState
|
||||
description={
|
||||
"We didn't mean for you to reach this page. Let's find your way back to building awesome applications."
|
||||
}
|
||||
action={
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.props.history.push("/");
|
||||
}}
|
||||
>
|
||||
{"Home"}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</NotFoundPageWrapper>
|
||||
<>
|
||||
<PageHeader />
|
||||
<Wrapper>
|
||||
<img
|
||||
src={PageUnavailableImage}
|
||||
alt="Page Unavailable"
|
||||
className="page-unavailable-img"
|
||||
></img>
|
||||
<div>
|
||||
<p className="bold-text">Page not found</p>
|
||||
<p>
|
||||
Either this page doesn't exist, or you don't have access to <br />
|
||||
this page.
|
||||
</p>
|
||||
<Button
|
||||
filled
|
||||
text="Go back to homepage"
|
||||
intent="primary"
|
||||
icon="arrow-right"
|
||||
iconAlignment="right"
|
||||
size="small"
|
||||
className="button-position"
|
||||
onClick={() => this.props.history.push(BASE_URL)}
|
||||
/>
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import FormDialogComponent from "components/editorComponents/form/FormDialogComp
|
|||
import { getCurrentUser } from "selectors/usersSelectors";
|
||||
import { User } from "constants/userConstants";
|
||||
import { useTable, useFlexLayout } from "react-table";
|
||||
|
||||
type OrgProps = {
|
||||
allOrgs: Organization[];
|
||||
changeOrgName: (value: string) => void;
|
||||
|
|
@ -51,6 +52,8 @@ type DropdownProps = {
|
|||
activeItem: string;
|
||||
userRoles: object;
|
||||
username: string;
|
||||
changeOrgUserRole: (orgId: string, role: string, username: string) => void;
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
const StyledDropDown = styled.div`
|
||||
|
|
@ -79,6 +82,90 @@ const StyledMenu = styled(Menu)`
|
|||
}
|
||||
`;
|
||||
|
||||
const RoleNameCell = (props: any) => {
|
||||
const {
|
||||
roleName,
|
||||
roles,
|
||||
username,
|
||||
isCurrentUser,
|
||||
isChangingRole,
|
||||
} = props.cellProps.row.original;
|
||||
|
||||
if (isCurrentUser) {
|
||||
return <div>{roleName}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={
|
||||
<Dropdown
|
||||
activeItem={roleName}
|
||||
userRoles={roles}
|
||||
username={username}
|
||||
changeOrgUserRole={props.changeOrgUserRole}
|
||||
orgId={props.orgId}
|
||||
/>
|
||||
}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<StyledDropDown>
|
||||
{roleName}
|
||||
<Icon icon="chevron-down" />
|
||||
{isChangingRole ? <Spinner size={20} /> : undefined}
|
||||
</StyledDropDown>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
const DeleteActionCell = (props: any) => {
|
||||
const { username, isCurrentUser, isDeleting } = props.cellProps.row.original;
|
||||
|
||||
return (
|
||||
!isCurrentUser &&
|
||||
(isDeleting ? (
|
||||
<Spinner size={20} />
|
||||
) : (
|
||||
<FormIcons.DELETE_ICON
|
||||
height={20}
|
||||
width={20}
|
||||
color={"grey"}
|
||||
background={"grey"}
|
||||
onClick={() => props.deleteOrgUser(props.orgId, username)}
|
||||
style={{ alignSelf: "center", cursor: "pointer" }}
|
||||
/>
|
||||
))
|
||||
);
|
||||
};
|
||||
|
||||
const Dropdown = (props: DropdownProps) => {
|
||||
return (
|
||||
<StyledMenu>
|
||||
{Object.entries(props.userRoles).map((role, index) => {
|
||||
const MenuContent = (
|
||||
<div>
|
||||
<span>
|
||||
<b>{role[0]}</b>
|
||||
</span>
|
||||
<div>{role[1]}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
multiline
|
||||
key={index}
|
||||
onClick={() =>
|
||||
props.changeOrgUserRole(props.orgId, role[0], props.username)
|
||||
}
|
||||
active={props.activeItem === role[0]}
|
||||
text={MenuContent}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export const OrgSettings = (props: PageProps) => {
|
||||
const {
|
||||
match: {
|
||||
|
|
@ -97,43 +184,7 @@ export const OrgSettings = (props: PageProps) => {
|
|||
roles: props.allRole,
|
||||
isCurrentUser: user.username === props.currentUser?.username,
|
||||
}));
|
||||
const data = React.useMemo(() => userTableData, [
|
||||
props.allUsers,
|
||||
props.allRole,
|
||||
]);
|
||||
|
||||
const RoleNameCell = (cellProps: any) => {
|
||||
const {
|
||||
roleName,
|
||||
roles,
|
||||
username,
|
||||
isCurrentUser,
|
||||
isChangingRole,
|
||||
} = cellProps.row.original;
|
||||
|
||||
if (isCurrentUser) {
|
||||
return <div>{roleName}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover
|
||||
content={
|
||||
<Dropdown
|
||||
activeItem={roleName}
|
||||
userRoles={roles}
|
||||
username={username}
|
||||
/>
|
||||
}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<StyledDropDown>
|
||||
{roleName}
|
||||
<Icon icon="chevron-down" />
|
||||
{isChangingRole ? <Spinner size={20} /> : undefined}
|
||||
</StyledDropDown>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
const data = React.useMemo(() => userTableData, [userTableData]);
|
||||
|
||||
const columns = React.useMemo(() => {
|
||||
return [
|
||||
|
|
@ -148,37 +199,19 @@ export const OrgSettings = (props: PageProps) => {
|
|||
{
|
||||
Header: "Role",
|
||||
accessor: "roleName",
|
||||
Cell: RoleNameCell,
|
||||
Cell: (cellProps: any) => {
|
||||
return RoleNameCell({ cellProps, changeOrgUserRole, orgId });
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Delete",
|
||||
accessor: "delete",
|
||||
Cell: (cellProps: any) => {
|
||||
const {
|
||||
username,
|
||||
isCurrentUser,
|
||||
isDeleting,
|
||||
} = cellProps.row.original;
|
||||
|
||||
return (
|
||||
!isCurrentUser &&
|
||||
(isDeleting ? (
|
||||
<Spinner size={20} />
|
||||
) : (
|
||||
<FormIcons.DELETE_ICON
|
||||
height={20}
|
||||
width={20}
|
||||
color={"grey"}
|
||||
background={"grey"}
|
||||
onClick={() => deleteOrgUser(orgId, username)}
|
||||
style={{ alignSelf: "center", cursor: "pointer" }}
|
||||
/>
|
||||
))
|
||||
);
|
||||
return DeleteActionCell({ cellProps, deleteOrgUser, orgId });
|
||||
},
|
||||
},
|
||||
];
|
||||
}, [props.allUsers, props.allRole]);
|
||||
}, [orgId, deleteOrgUser, changeOrgUserRole]);
|
||||
|
||||
const currentOrg = allOrgs.find(org => org.organization.id === orgId);
|
||||
const currentOrgName = currentOrg?.organization.name ?? "";
|
||||
|
|
@ -203,33 +236,6 @@ export const OrgSettings = (props: PageProps) => {
|
|||
getAllApplication();
|
||||
}, [orgId, fetchUser, fetchAllRoles, getAllApplication]);
|
||||
|
||||
const Dropdown = (props: DropdownProps) => {
|
||||
return (
|
||||
<StyledMenu>
|
||||
{Object.entries(props.userRoles).map((role, index) => {
|
||||
const MenuContent = (
|
||||
<div>
|
||||
<span>
|
||||
<b>{role[0]}</b>
|
||||
</span>
|
||||
<div>{role[1]}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
multiline
|
||||
key={index}
|
||||
onClick={() => changeOrgUserRole(orgId, role[0], props.username)}
|
||||
active={props.activeItem === role[0]}
|
||||
text={MenuContent}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledMenu>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageSectionHeader>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import {
|
|||
} from "constants/ReduxActionConstants";
|
||||
import { Organization } from "constants/orgConstants";
|
||||
import { ERROR_MESSAGE_CREATE_APPLICATION } from "constants/messages";
|
||||
import { getApplicationPayload } from "mockComponentProps/ApplicationPayloads";
|
||||
|
||||
const initialState: ApplicationsReduxState = {
|
||||
isFetchingApplications: false,
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ const orgReducer = createReducer(initialState, {
|
|||
action: ReduxAction<{ username: string }>,
|
||||
) => {
|
||||
const _orgUsers = state.orgUsers.map((user: OrgUser) => {
|
||||
if (user.username == action.payload.username) {
|
||||
if (user.username === action.payload.username) {
|
||||
return {
|
||||
...user,
|
||||
isChangingRole: true,
|
||||
|
|
@ -119,7 +119,7 @@ const orgReducer = createReducer(initialState, {
|
|||
action: ReduxAction<{ username: string }>,
|
||||
) => {
|
||||
const _orgUsers = state.orgUsers.map((user: OrgUser) => {
|
||||
if (user.username == action.payload.username) {
|
||||
if (user.username === action.payload.username) {
|
||||
return {
|
||||
...user,
|
||||
isDeleting: true,
|
||||
|
|
|
|||
|
|
@ -83,10 +83,6 @@ export function* getAllApplicationSaga() {
|
|||
type: ReduxActionTypes.FETCH_USER_APPLICATIONS_ORGS_SUCCESS,
|
||||
payload: organizationApplication,
|
||||
});
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,
|
||||
payload: response.data.user,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
|
|
|
|||
|
|
@ -72,6 +72,27 @@ export function* createUserSaga(
|
|||
}
|
||||
}
|
||||
|
||||
export function* getCurrentUserSaga() {
|
||||
try {
|
||||
const response: ApiResponse = yield call(UserApi.getCurrentUser);
|
||||
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.FETCH_USER_DETAILS_ERROR,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function* forgotPasswordSaga(
|
||||
action: ReduxActionWithPromise<ForgotPasswordRequest>,
|
||||
) {
|
||||
|
|
@ -326,6 +347,7 @@ export function* logoutSaga() {
|
|||
export default function* userSagas() {
|
||||
yield all([
|
||||
takeLatest(ReduxActionTypes.CREATE_USER_INIT, createUserSaga),
|
||||
takeLatest(ReduxActionTypes.FETCH_USER_INIT, getCurrentUserSaga),
|
||||
takeLatest(ReduxActionTypes.FORGOT_PASSWORD_INIT, forgotPasswordSaga),
|
||||
takeLatest(ReduxActionTypes.RESET_USER_PASSWORD_INIT, resetPasswordSaga),
|
||||
takeLatest(
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user