mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
chore: update react-router to v6 (#946)
* refactor: fix child selector warnings * refactor: update react-router-dom * refactor: use BrowserRouter as in react-router docs * refactor: replace Redirect with Navigate * refactor: replace Switch with Routes * refactor: replace useHistory with useNavigate * refactor: replace useParams types with useRequiredPathParam * refactor: replace NavLink activeStyle with callback * refactor: fix matchPath arg order * refactor: Remove unused link state * refactor: delete broken snapshot test * refactor: render 404 page without redirect * refactor: normalize path parameter names * refactor: fix Route component usage
This commit is contained in:
parent
4c42639a62
commit
d8143c6ff4
@ -55,7 +55,6 @@
|
||||
"@types/node": "17.0.18",
|
||||
"@types/react": "17.0.44",
|
||||
"@types/react-dom": "17.0.16",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-test-renderer": "17.0.2",
|
||||
"@types/react-timeago": "4.1.3",
|
||||
"@types/semver": "^7.3.9",
|
||||
@ -81,7 +80,7 @@
|
||||
"react-dnd-html5-backend": "15.1.3",
|
||||
"react-dom": "17.0.2",
|
||||
"react-hooks-global-state": "1.0.2",
|
||||
"react-router-dom": "5.3.1",
|
||||
"react-router-dom": "6.3.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"react-timeago": "6.2.1",
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
import { createElement } from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { FeedbackNPS } from 'component/feedback/FeedbackNPS/FeedbackNPS';
|
||||
import { LayoutPicker } from 'component/layout/LayoutPicker/LayoutPicker';
|
||||
import Loader from 'component/common/Loader/Loader';
|
||||
import NotFound from 'component/common/NotFound/NotFound';
|
||||
import ProtectedRoute from 'component/common/ProtectedRoute/ProtectedRoute';
|
||||
import { ProtectedRoute } from 'component/common/ProtectedRoute/ProtectedRoute';
|
||||
import SWRProvider from 'component/providers/SWRProvider/SWRProvider';
|
||||
import ToastRenderer from 'component/common/ToastRenderer/ToastRenderer';
|
||||
import { routes } from 'component/menu/routes';
|
||||
import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails';
|
||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||
import { SplashPageRedirect } from 'component/splash/SplashPageRedirect/SplashPageRedirect';
|
||||
import { IRoute } from 'interfaces/route';
|
||||
import styles from 'component/styles.module.scss';
|
||||
|
||||
export const App = () => {
|
||||
@ -21,33 +19,8 @@ export const App = () => {
|
||||
const isLoggedIn = Boolean(user?.id);
|
||||
const hasFetchedAuth = Boolean(authDetails || user);
|
||||
|
||||
const isUnauthorized = (): boolean => {
|
||||
return !isLoggedIn;
|
||||
};
|
||||
|
||||
const renderRoute = (route: IRoute) => {
|
||||
if (route.type === 'protected') {
|
||||
const unauthorized = isUnauthorized();
|
||||
return (
|
||||
<ProtectedRoute
|
||||
key={route.path}
|
||||
path={route.path}
|
||||
component={route.component}
|
||||
unauthorized={unauthorized}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Route
|
||||
key={route.path}
|
||||
path={route.path}
|
||||
render={() => createElement(route.component, {}, null)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SWRProvider isUnauthorized={isUnauthorized}>
|
||||
<SWRProvider isUnauthorized={!isLoggedIn}>
|
||||
<ConditionallyRender
|
||||
condition={!hasFetchedAuth}
|
||||
show={<Loader />}
|
||||
@ -55,19 +28,24 @@ export const App = () => {
|
||||
<div className={styles.container}>
|
||||
<ToastRenderer />
|
||||
<LayoutPicker>
|
||||
<Switch>
|
||||
<ProtectedRoute
|
||||
exact
|
||||
<Routes>
|
||||
{routes.map(route => (
|
||||
<Route
|
||||
key={route.path}
|
||||
path={route.path}
|
||||
element={
|
||||
<ProtectedRoute route={route} />
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<Route
|
||||
path="/"
|
||||
unauthorized={isUnauthorized()}
|
||||
component={() => (
|
||||
<Redirect to="/features" />
|
||||
)}
|
||||
element={
|
||||
<Navigate to="/features" replace />
|
||||
}
|
||||
/>
|
||||
{routes.map(renderRoute)}
|
||||
<Route path="/404" component={NotFound} />
|
||||
<Redirect to="/404" />
|
||||
</Switch>
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
<FeedbackNPS openUrl="http://feedback.unleash.run" />
|
||||
<SplashPageRedirect />
|
||||
</LayoutPicker>
|
||||
|
||||
@ -16,7 +16,7 @@ import { AddonParameters } from './AddonParameters/AddonParameters';
|
||||
import { AddonEvents } from './AddonEvents/AddonEvents';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useAddonsApi from 'hooks/api/actions/useAddonsApi/useAddonsApi';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
@ -50,7 +50,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
|
||||
}) => {
|
||||
const { createAddon, updateAddon } = useAddonsApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
const [formValues, setFormValues] = useState(initialValues);
|
||||
@ -119,7 +119,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
const onSubmit: FormEventHandler<HTMLFormElement> = async event => {
|
||||
@ -152,14 +152,14 @@ export const AddonForm: VFC<IAddonFormProps> = ({
|
||||
try {
|
||||
if (editMode) {
|
||||
await updateAddon(formValues);
|
||||
history.push('/addons');
|
||||
navigate('/addons');
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Addon updated successfully',
|
||||
});
|
||||
} else {
|
||||
await createAddon(formValues);
|
||||
history.push('/addons');
|
||||
navigate('/addons');
|
||||
setToastData({
|
||||
type: 'success',
|
||||
confetti: true,
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
ListItemText,
|
||||
} from '@mui/material';
|
||||
import { CREATE_ADDON } from 'component/providers/AccessProvider/permissions';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
|
||||
interface IProvider {
|
||||
@ -29,7 +29,7 @@ export const AvailableAddons = ({
|
||||
providers,
|
||||
getAddonIcon,
|
||||
}: IAvailableAddonsProps) => {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const renderProvider = (provider: IProvider) => (
|
||||
<ListItem key={provider.name}>
|
||||
@ -41,9 +41,7 @@ export const AvailableAddons = ({
|
||||
<ListItemSecondaryAction>
|
||||
<PermissionButton
|
||||
permission={CREATE_ADDON}
|
||||
onClick={() =>
|
||||
history.push(`/addons/create/${provider.name}`)
|
||||
}
|
||||
onClick={() => navigate(`/addons/create/${provider.name}`)}
|
||||
>
|
||||
Configure
|
||||
</PermissionButton>
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
DELETE_ADDON,
|
||||
UPDATE_ADDON,
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||
import useToast from 'hooks/useToast';
|
||||
@ -32,7 +32,7 @@ export const ConfiguredAddons = ({ getAddonIcon }: IConfigureAddonsProps) => {
|
||||
const { updateAddon, removeAddon } = useAddonsApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const [showDelete, setShowDelete] = useState(false);
|
||||
const [deletedAddon, setDeletedAddon] = useState<IAddon>({
|
||||
id: 0,
|
||||
@ -124,7 +124,7 @@ export const ConfiguredAddons = ({ getAddonIcon }: IConfigureAddonsProps) => {
|
||||
<PermissionIconButton
|
||||
permission={UPDATE_ADDON}
|
||||
tooltip="Edit Addon"
|
||||
onClick={() => history.push(`/addons/edit/${addon.id}`)}
|
||||
onClick={() => navigate(`/addons/edit/${addon.id}`)}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||
import { AddonForm } from '../AddonForm/AddonForm';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import { IAddon } from 'interfaces/addons';
|
||||
|
||||
interface IAddonCreateParams {
|
||||
providerId: string;
|
||||
}
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const DEFAULT_DATA = {
|
||||
provider: '',
|
||||
@ -17,8 +13,7 @@ export const DEFAULT_DATA = {
|
||||
} as unknown as IAddon; // TODO: improve type
|
||||
|
||||
export const CreateAddon = () => {
|
||||
const { providerId } = useParams<IAddonCreateParams>();
|
||||
|
||||
const providerId = useRequiredPathParam('providerId');
|
||||
const { providers, refetchAddons } = useAddons();
|
||||
|
||||
const editMode = false;
|
||||
|
||||
@ -1,17 +1,12 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||
import { AddonForm } from '../AddonForm/AddonForm';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import { IAddon } from 'interfaces/addons';
|
||||
import { DEFAULT_DATA } from '../CreateAddon/CreateAddon';
|
||||
|
||||
interface IAddonEditParams {
|
||||
addonId: string;
|
||||
}
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const EditAddon = () => {
|
||||
const { addonId } = useParams<IAddonEditParams>();
|
||||
|
||||
const addonId = useRequiredPathParam('addonId');
|
||||
const { providers, addons, refetchAddons } = useAddons();
|
||||
|
||||
const editMode = true;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button } from '@mui/material';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
@ -20,7 +20,7 @@ export const ApiTokenPage = () => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<PageContent
|
||||
@ -35,7 +35,7 @@ export const ApiTokenPage = () => {
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
history.push('/admin/api/create-token')
|
||||
navigate('/admin/api/create-token')
|
||||
}
|
||||
data-testid={CREATE_API_TOKEN_BUTTON}
|
||||
>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import ApiTokenForm from '../ApiTokenForm/ApiTokenForm';
|
||||
import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||
import useApiTokensApi from 'hooks/api/actions/useApiTokensApi/useApiTokensApi';
|
||||
@ -15,7 +15,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
export const CreateApiToken = () => {
|
||||
const { setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
const [token, setToken] = useState('');
|
||||
|
||||
@ -57,7 +57,7 @@ export const CreateApiToken = () => {
|
||||
|
||||
const closeConfirm = () => {
|
||||
setShowConfirm(false);
|
||||
history.push('/admin/api');
|
||||
navigate('/admin/api');
|
||||
};
|
||||
|
||||
const formatApiCode = () => {
|
||||
@ -70,7 +70,7 @@ export const CreateApiToken = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
|
||||
const render = () => <Redirect to="/admin/users" />;
|
||||
const render = () => <Navigate to="/admin/users" replace />;
|
||||
|
||||
render.propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
|
||||
@ -19,6 +19,14 @@ const activeNavLinkStyle: React.CSSProperties = {
|
||||
padding: '0.8rem 1.5rem',
|
||||
};
|
||||
|
||||
const createNavLinkStyle = (props: {
|
||||
isActive: boolean;
|
||||
}): React.CSSProperties => {
|
||||
return props.isActive
|
||||
? { ...navLinkStyle, ...activeNavLinkStyle }
|
||||
: navLinkStyle;
|
||||
};
|
||||
|
||||
function AdminMenu() {
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { pathname } = useLocation();
|
||||
@ -36,11 +44,7 @@ function AdminMenu() {
|
||||
<Tab
|
||||
value="/admin/users"
|
||||
label={
|
||||
<NavLink
|
||||
to="/admin/users"
|
||||
activeStyle={activeNavLinkStyle}
|
||||
style={navLinkStyle}
|
||||
>
|
||||
<NavLink to="/admin/users" style={createNavLinkStyle}>
|
||||
<span>Users</span>
|
||||
</NavLink>
|
||||
}
|
||||
@ -51,8 +55,7 @@ function AdminMenu() {
|
||||
label={
|
||||
<NavLink
|
||||
to="/admin/roles"
|
||||
activeStyle={activeNavLinkStyle}
|
||||
style={navLinkStyle}
|
||||
style={createNavLinkStyle}
|
||||
>
|
||||
<span>PROJECT ROLES</span>
|
||||
</NavLink>
|
||||
@ -63,11 +66,7 @@ function AdminMenu() {
|
||||
<Tab
|
||||
value="/admin/api"
|
||||
label={
|
||||
<NavLink
|
||||
to="/admin/api"
|
||||
activeStyle={activeNavLinkStyle}
|
||||
style={navLinkStyle}
|
||||
>
|
||||
<NavLink to="/admin/api" style={createNavLinkStyle}>
|
||||
API Access
|
||||
</NavLink>
|
||||
}
|
||||
@ -75,11 +74,7 @@ function AdminMenu() {
|
||||
<Tab
|
||||
value="/admin/auth"
|
||||
label={
|
||||
<NavLink
|
||||
to="/admin/auth"
|
||||
activeStyle={activeNavLinkStyle}
|
||||
style={navLinkStyle}
|
||||
>
|
||||
<NavLink to="/admin/auth" style={createNavLinkStyle}>
|
||||
Single Sign-On
|
||||
</NavLink>
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import useProjectRolesApi from 'hooks/api/actions/useProjectRolesApi/useProjectRolesApi';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import ProjectRoleForm from '../ProjectRoleForm/ProjectRoleForm';
|
||||
import useProjectRoleForm from '../hooks/useProjectRoleForm';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
@ -12,7 +12,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
const CreateProjectRole = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
roleName,
|
||||
roleDesc,
|
||||
@ -43,7 +43,7 @@ const CreateProjectRole = () => {
|
||||
const payload = getProjectRolePayload();
|
||||
try {
|
||||
await createRole(payload);
|
||||
history.push('/admin/roles');
|
||||
navigate('/admin/roles');
|
||||
setToastData({
|
||||
title: 'Project role created',
|
||||
text: 'Now you can start assigning your project roles to project members.',
|
||||
@ -66,7 +66,7 @@ const CreateProjectRole = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -7,19 +7,19 @@ import useProjectRole from 'hooks/api/getters/useProjectRole/useProjectRole';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { IPermission } from 'interfaces/user';
|
||||
import { useParams, useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useProjectRoleForm from '../hooks/useProjectRoleForm';
|
||||
import ProjectRoleForm from '../ProjectRoleForm/ProjectRoleForm';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const EditProjectRole = () => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { role } = useProjectRole(projectId);
|
||||
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const { role } = useProjectRole(id);
|
||||
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
roleName,
|
||||
roleDesc,
|
||||
@ -66,7 +66,7 @@ const EditProjectRole = () => {
|
||||
--data-raw '${JSON.stringify(getProjectRolePayload(), undefined, 2)}'`;
|
||||
};
|
||||
|
||||
const { refetch } = useProjectRole(id);
|
||||
const { refetch } = useProjectRole(projectId);
|
||||
const { editRole, loading } = useProjectRolesApi();
|
||||
|
||||
const handleSubmit = async (e: Event) => {
|
||||
@ -78,9 +78,9 @@ const EditProjectRole = () => {
|
||||
|
||||
if (validName && validPermissions) {
|
||||
try {
|
||||
await editRole(id, payload);
|
||||
await editRole(projectId, payload);
|
||||
refetch();
|
||||
history.push('/admin/roles');
|
||||
navigate('/admin/roles');
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Project role updated',
|
||||
@ -94,7 +94,7 @@ const EditProjectRole = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -5,7 +5,7 @@ import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import SupervisedUserCircleIcon from '@mui/icons-material/SupervisedUserCircle';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { IProjectRole } from 'interfaces/role';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import React from 'react';
|
||||
|
||||
interface IRoleListItemProps {
|
||||
@ -27,7 +27,7 @@ const RoleListItem = ({
|
||||
setCurrentRole,
|
||||
setDelDialog,
|
||||
}: IRoleListItemProps) => {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
@ -52,7 +52,7 @@ const RoleListItem = ({
|
||||
data-loading
|
||||
disabled={type === BUILTIN_ROLE_TYPE}
|
||||
onClick={() => {
|
||||
history.push(`/admin/roles/${id}/edit`);
|
||||
navigate(`/admin/roles/${id}/edit`);
|
||||
}}
|
||||
permission={ADMIN}
|
||||
tooltip="Edit role"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Button } from '@mui/material';
|
||||
import { useContext } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
@ -14,7 +14,7 @@ import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
|
||||
const ProjectRoles = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { classes: styles } = useStyles();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -32,7 +32,7 @@ const ProjectRoles = () => {
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
history.push(
|
||||
navigate(
|
||||
'/admin/create-project-role'
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import UserForm from '../UserForm/UserForm';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
|
||||
@ -15,7 +15,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
const CreateUser = () => {
|
||||
const { setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
name,
|
||||
setName,
|
||||
@ -59,7 +59,7 @@ const CreateUser = () => {
|
||||
};
|
||||
const closeConfirm = () => {
|
||||
setShowConfirm(false);
|
||||
history.push('/admin/user-admin');
|
||||
navigate('/admin/users');
|
||||
};
|
||||
|
||||
const formatApiCode = () => {
|
||||
@ -72,7 +72,7 @@ const CreateUser = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import UserForm from '../UserForm/UserForm';
|
||||
import useAddUserForm from '../hooks/useAddUserForm';
|
||||
import { scrollToTop } from 'component/common/util';
|
||||
@ -12,6 +12,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useUserInfo from 'hooks/api/getters/useUserInfo/useUserInfo';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const EditUser = () => {
|
||||
useEffect(() => {
|
||||
@ -19,10 +20,10 @@ const EditUser = () => {
|
||||
}, []);
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const id = useRequiredPathParam('id');
|
||||
const { user, refetch } = useUserInfo(id);
|
||||
const { updateUser, userLoading: loading } = useAdminUsersApi();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
name,
|
||||
setName,
|
||||
@ -56,7 +57,7 @@ const EditUser = () => {
|
||||
try {
|
||||
await updateUser({ ...payload, id });
|
||||
refetch();
|
||||
history.push('/admin/users');
|
||||
navigate('/admin/users');
|
||||
setToastData({
|
||||
title: 'User information updated',
|
||||
type: 'success',
|
||||
@ -68,7 +69,7 @@ const EditUser = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -9,13 +9,13 @@ import { Button } from '@mui/material';
|
||||
import { TableActions } from 'component/common/Table/TableActions/TableActions';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { useStyles } from './UserAdmin.styles';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
|
||||
|
||||
const UsersAdmin = () => {
|
||||
const [search, setSearch] = useState('');
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
@ -41,9 +41,7 @@ const UsersAdmin = () => {
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
history.push(
|
||||
'/admin/create-user'
|
||||
)
|
||||
navigate('/admin/create-user')
|
||||
}
|
||||
>
|
||||
New user
|
||||
|
||||
@ -16,11 +16,11 @@ export const useStyles = makeStyles()(theme => ({
|
||||
backgroundColor: unleashGrey[200],
|
||||
fontWeight: 'normal',
|
||||
border: 0,
|
||||
'&:first-child': {
|
||||
'&:first-of-type': {
|
||||
borderTopLeftRadius: '8px',
|
||||
borderBottomLeftRadius: '8px',
|
||||
},
|
||||
'&:last-child': {
|
||||
'&:last-of-type': {
|
||||
borderTopRightRadius: '8px',
|
||||
borderBottomRightRadius: '8px',
|
||||
},
|
||||
|
||||
@ -13,7 +13,7 @@ import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { IUser } from 'interfaces/user';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ILocationSettings } from 'hooks/useLocationSettings';
|
||||
import { formatDateYMD } from 'utils/formatDate';
|
||||
import { Highlighter } from 'component/common/Highlighter/Highlighter';
|
||||
@ -37,7 +37,7 @@ const UserListItem = ({
|
||||
search,
|
||||
}: IUserListItemProps) => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
@ -88,7 +88,7 @@ const UserListItem = ({
|
||||
<IconButton
|
||||
data-loading
|
||||
onClick={() =>
|
||||
history.push(`/admin/users/${user.id}/edit`)
|
||||
navigate(`/admin/users/${user.id}/edit`)
|
||||
}
|
||||
size="large"
|
||||
>
|
||||
|
||||
@ -1,158 +0,0 @@
|
||||
import { ApplicationEdit } from './ApplicationEdit';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import UIProvider from 'component/providers/UIProvider/UIProvider';
|
||||
import { ThemeProvider } from 'themes/ThemeProvider';
|
||||
import { AccessProviderMock } from 'component/providers/AccessProvider/AccessProviderMock';
|
||||
|
||||
test('renders correctly if no application', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<AccessProviderMock permissions={[{ permission: ADMIN }]}>
|
||||
<ThemeProvider>
|
||||
<UIProvider>
|
||||
<MemoryRouter initialEntries={['/test']}>
|
||||
<ApplicationEdit
|
||||
fetchApplication={() => Promise.resolve({})}
|
||||
storeApplicationMetaData={jest.fn()}
|
||||
deleteApplication={jest.fn()}
|
||||
locationSettings={{ locale: 'en-GB' }}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</UIProvider>
|
||||
</ThemeProvider>
|
||||
</AccessProviderMock>
|
||||
)
|
||||
.toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders correctly without permission', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<MemoryRouter>
|
||||
<ThemeProvider>
|
||||
<UIProvider>
|
||||
<AccessProviderMock
|
||||
permissions={[{ permission: ADMIN }]}
|
||||
>
|
||||
<ApplicationEdit
|
||||
fetchApplication={() => Promise.resolve({})}
|
||||
storeApplicationMetaData={jest.fn()}
|
||||
deleteApplication={jest.fn()}
|
||||
application={{
|
||||
appName: 'test-app',
|
||||
instances: [
|
||||
{
|
||||
instanceId: 'instance-1',
|
||||
clientIp: '123.123.123.123',
|
||||
lastSeen: '2017-02-23T15:56:49',
|
||||
sdkVersion: '4.0',
|
||||
},
|
||||
],
|
||||
strategies: [
|
||||
{
|
||||
name: 'StrategyA',
|
||||
description: 'A description',
|
||||
},
|
||||
{
|
||||
name: 'StrategyB',
|
||||
description: 'B description',
|
||||
notFound: true,
|
||||
},
|
||||
],
|
||||
seenToggles: [
|
||||
{
|
||||
name: 'ToggleA',
|
||||
description: 'this is A toggle',
|
||||
enabled: true,
|
||||
project: 'default',
|
||||
},
|
||||
{
|
||||
name: 'ToggleB',
|
||||
description: 'this is B toggle',
|
||||
enabled: false,
|
||||
notFound: true,
|
||||
project: 'default',
|
||||
},
|
||||
],
|
||||
url: 'http://example.org',
|
||||
description: 'app description',
|
||||
}}
|
||||
locationSettings={{ locale: 'en-GB' }}
|
||||
/>
|
||||
</AccessProviderMock>
|
||||
</UIProvider>
|
||||
</ThemeProvider>
|
||||
</MemoryRouter>
|
||||
)
|
||||
.toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders correctly with permissions', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<MemoryRouter>
|
||||
<ThemeProvider>
|
||||
<UIProvider>
|
||||
<AccessProviderMock
|
||||
permissions={[{ permission: ADMIN }]}
|
||||
>
|
||||
<ApplicationEdit
|
||||
fetchApplication={() => Promise.resolve({})}
|
||||
storeApplicationMetaData={jest.fn()}
|
||||
deleteApplication={jest.fn()}
|
||||
application={{
|
||||
appName: 'test-app',
|
||||
instances: [
|
||||
{
|
||||
instanceId: 'instance-1',
|
||||
clientIp: '123.123.123.123',
|
||||
lastSeen: '2017-02-23T15:56:49',
|
||||
sdkVersion: '4.0',
|
||||
},
|
||||
],
|
||||
strategies: [
|
||||
{
|
||||
name: 'StrategyA',
|
||||
description: 'A description',
|
||||
},
|
||||
{
|
||||
name: 'StrategyB',
|
||||
description: 'B description',
|
||||
notFound: true,
|
||||
},
|
||||
],
|
||||
seenToggles: [
|
||||
{
|
||||
name: 'ToggleA',
|
||||
description: 'this is A toggle',
|
||||
enabled: true,
|
||||
project: 'default',
|
||||
},
|
||||
{
|
||||
name: 'ToggleB',
|
||||
description: 'this is B toggle',
|
||||
enabled: false,
|
||||
notFound: true,
|
||||
project: 'default',
|
||||
},
|
||||
],
|
||||
url: 'http://example.org',
|
||||
description: 'app description',
|
||||
}}
|
||||
locationSettings={{ locale: 'en-GB' }}
|
||||
/>
|
||||
</AccessProviderMock>
|
||||
</UIProvider>
|
||||
</ThemeProvider>
|
||||
</MemoryRouter>
|
||||
)
|
||||
.toJSON();
|
||||
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
@ -20,16 +20,17 @@ import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import useApplicationsApi from 'hooks/api/actions/useApplicationsApi/useApplicationsApi';
|
||||
import useApplication from 'hooks/api/getters/useApplication/useApplication';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import useToast from 'hooks/useToast';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { formatDateYMD } from 'utils/formatDate';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const ApplicationEdit = () => {
|
||||
const history = useHistory();
|
||||
const { name } = useParams<{ name: string }>();
|
||||
const navigate = useNavigate();
|
||||
const name = useRequiredPathParam('name');
|
||||
const { application, loading } = useApplication(name);
|
||||
const { appName, url, description, icon = 'apps', createdAt } = application;
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
@ -54,7 +55,7 @@ export const ApplicationEdit = () => {
|
||||
text: 'Application deleted successfully',
|
||||
type: 'success',
|
||||
});
|
||||
history.push('/applications');
|
||||
navigate('/applications');
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly if no application 1`] = `
|
||||
<div>
|
||||
<p>
|
||||
Loading...
|
||||
</p>
|
||||
<span
|
||||
className="MuiLinearProgress-root MuiLinearProgress-colorPrimary MuiLinearProgress-indeterminate mui-130qhmc-MuiLinearProgress-root"
|
||||
role="progressbar"
|
||||
>
|
||||
<span
|
||||
className="MuiLinearProgress-bar MuiLinearProgress-barColorPrimary MuiLinearProgress-bar1Indeterminate mui-yt7v2r-MuiLinearProgress-bar1"
|
||||
style={Object {}}
|
||||
/>
|
||||
<span
|
||||
className="MuiLinearProgress-bar MuiLinearProgress-barColorPrimary MuiLinearProgress-bar2Indeterminate mui-1wjhhwq-MuiLinearProgress-bar2"
|
||||
style={Object {}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders correctly with permissions 1`] = `
|
||||
<div>
|
||||
<p>
|
||||
Loading...
|
||||
</p>
|
||||
<span
|
||||
className="MuiLinearProgress-root MuiLinearProgress-colorPrimary MuiLinearProgress-indeterminate mui-130qhmc-MuiLinearProgress-root"
|
||||
role="progressbar"
|
||||
>
|
||||
<span
|
||||
className="MuiLinearProgress-bar MuiLinearProgress-barColorPrimary MuiLinearProgress-bar1Indeterminate mui-yt7v2r-MuiLinearProgress-bar1"
|
||||
style={Object {}}
|
||||
/>
|
||||
<span
|
||||
className="MuiLinearProgress-bar MuiLinearProgress-barColorPrimary MuiLinearProgress-bar2Indeterminate mui-1wjhhwq-MuiLinearProgress-bar2"
|
||||
style={Object {}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders correctly without permission 1`] = `
|
||||
<div>
|
||||
<p>
|
||||
Loading...
|
||||
</p>
|
||||
<span
|
||||
className="MuiLinearProgress-root MuiLinearProgress-colorPrimary MuiLinearProgress-indeterminate mui-130qhmc-MuiLinearProgress-root"
|
||||
role="progressbar"
|
||||
>
|
||||
<span
|
||||
className="MuiLinearProgress-bar MuiLinearProgress-barColorPrimary MuiLinearProgress-bar1Indeterminate mui-yt7v2r-MuiLinearProgress-bar1"
|
||||
style={Object {}}
|
||||
/>
|
||||
<span
|
||||
className="MuiLinearProgress-bar MuiLinearProgress-barColorPrimary MuiLinearProgress-bar2Indeterminate mui-1wjhhwq-MuiLinearProgress-bar2"
|
||||
style={Object {}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
@ -1,5 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
Grid,
|
||||
List,
|
||||
@ -25,10 +25,11 @@ import useApplication from 'hooks/api/getters/useApplication/useApplication';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { formatDateYMDHMS } from 'utils/formatDate';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const ApplicationView = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { name } = useParams<{ name: string }>();
|
||||
const name = useRequiredPathParam('name');
|
||||
const { application } = useApplication(name);
|
||||
const { locationSettings } = useLocationSettings();
|
||||
const { instances, strategies, seenToggles } = application;
|
||||
@ -147,7 +148,7 @@ export const ApplicationView = () => {
|
||||
permission: CREATE_STRATEGY,
|
||||
})}
|
||||
elseShow={foundListItem({
|
||||
viewUrl: '/strategies/view',
|
||||
viewUrl: '/strategies',
|
||||
name,
|
||||
Icon: Extension,
|
||||
description,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
|
||||
const RedirectArchive = () => {
|
||||
return <Redirect to="/archive" />;
|
||||
return <Navigate to="/archive" replace />;
|
||||
};
|
||||
|
||||
export default RedirectArchive;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import PermissionButton from '../PermissionButton/PermissionButton';
|
||||
@ -20,7 +20,7 @@ const EnvironmentStrategyDialog = ({
|
||||
onClose,
|
||||
}: IEnvironmentStrategyDialogProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const createStrategyPath = formatCreateStrategyPath(
|
||||
projectId,
|
||||
@ -31,7 +31,7 @@ const EnvironmentStrategyDialog = ({
|
||||
|
||||
const onClick = () => {
|
||||
onClose();
|
||||
history.push(createStrategyPath);
|
||||
navigate(createStrategyPath);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import { useLocation, Navigate } from 'react-router-dom';
|
||||
|
||||
export const LoginRedirect = () => {
|
||||
const { pathname, search } = useLocation();
|
||||
|
||||
const redirect = encodeURIComponent(pathname + search);
|
||||
const loginLink = `/login?redirect=${redirect}`;
|
||||
|
||||
return <Navigate to={loginLink} replace />;
|
||||
};
|
||||
@ -8,6 +8,7 @@ export const useStyles = makeStyles()({
|
||||
minHeight: '100vh',
|
||||
padding: '2rem',
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
backgroundColor: '#fff',
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
import { Button, Typography } from '@mui/material';
|
||||
import { useHistory } from 'react-router';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
import { ReactComponent as LogoIcon } from 'assets/icons/logoBg.svg';
|
||||
|
||||
import { useStyles } from './NotFound.styles';
|
||||
|
||||
const NotFound = () => {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
const onClickHome = () => {
|
||||
history.push('/');
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
const onClickBack = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,24 +1,18 @@
|
||||
import { VFC } from 'react';
|
||||
import { Route, useLocation, Redirect, RouteProps } from 'react-router-dom';
|
||||
import { IRoute } from 'interfaces/route';
|
||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||
import { LoginRedirect } from 'component/common/LoginRedirect/LoginRedirect';
|
||||
|
||||
interface IProtectedRouteProps {
|
||||
unauthorized?: boolean;
|
||||
route: IRoute;
|
||||
}
|
||||
|
||||
const ProtectedRoute: VFC<IProtectedRouteProps & RouteProps> = ({
|
||||
component: Component,
|
||||
unauthorized,
|
||||
...rest
|
||||
}) => {
|
||||
const { pathname, search } = useLocation();
|
||||
const redirect = encodeURIComponent(pathname + search);
|
||||
const loginLink = `/login?redirect=${redirect}`;
|
||||
export const ProtectedRoute = ({ route }: IProtectedRouteProps) => {
|
||||
const { user } = useAuthUser();
|
||||
const isLoggedIn = Boolean(user?.id);
|
||||
|
||||
return unauthorized ? (
|
||||
<Route {...rest} render={() => <Redirect to={loginLink} />} />
|
||||
) : (
|
||||
<Route {...rest} component={Component} />
|
||||
);
|
||||
if (!isLoggedIn && route.type === 'protected') {
|
||||
return <LoginRedirect />;
|
||||
}
|
||||
|
||||
return <route.component />;
|
||||
};
|
||||
|
||||
export default ProtectedRoute;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useContext, useState, VFC } from 'react';
|
||||
import { Add, Album, Delete, Edit } from '@mui/icons-material';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
@ -35,7 +35,7 @@ const ContextList: VFC = () => {
|
||||
const { context, refetchUnleashContext } = useUnleashContext();
|
||||
const { removeContext } = useContextsApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
const onDeleteContext = async () => {
|
||||
@ -83,7 +83,7 @@ const ContextList: VFC = () => {
|
||||
<Tooltip title="Edit context field">
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
history.push(`/context/edit/${field.name}`)
|
||||
navigate(`/context/edit/${field.name}`)
|
||||
}
|
||||
size="large"
|
||||
>
|
||||
@ -120,7 +120,7 @@ const ContextList: VFC = () => {
|
||||
show={
|
||||
<Tooltip title="Add context type">
|
||||
<IconButton
|
||||
onClick={() => history.push('/context/create')}
|
||||
onClick={() => navigate('/context/create')}
|
||||
size="large"
|
||||
>
|
||||
<Add />
|
||||
@ -129,7 +129,7 @@ const ContextList: VFC = () => {
|
||||
}
|
||||
elseShow={
|
||||
<Button
|
||||
onClick={() => history.push('/context/create')}
|
||||
onClick={() => navigate('/context/create')}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
>
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { CreateUnleashContext } from 'component/context/CreateUnleashContext/CreateUnleashContext';
|
||||
|
||||
export const CreateUnleashContextPage = () => {
|
||||
const { push, goBack } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<CreateUnleashContext
|
||||
onSubmit={() => push('/context')}
|
||||
onCancel={() => goBack()}
|
||||
onSubmit={() => navigate('/context')}
|
||||
onCancel={() => navigate(-1)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -6,11 +6,12 @@ import useContext from 'hooks/api/getters/useContext/useContext';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { useEffect } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { scrollToTop } from 'component/common/util';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { ContextForm } from '../ContextForm/ContextForm';
|
||||
import { useContextForm } from '../hooks/useContextForm';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const EditContext = () => {
|
||||
useEffect(() => {
|
||||
@ -19,10 +20,10 @@ export const EditContext = () => {
|
||||
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { name } = useParams<{ name: string }>();
|
||||
const name = useRequiredPathParam('name');
|
||||
const { context, refetch } = useContext(name);
|
||||
const { updateContext, loading } = useContextsApi();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
contextName,
|
||||
contextDesc,
|
||||
@ -59,7 +60,7 @@ export const EditContext = () => {
|
||||
try {
|
||||
await updateContext(payload);
|
||||
refetch();
|
||||
history.push('/context');
|
||||
navigate('/context');
|
||||
setToastData({
|
||||
title: 'Context information updated',
|
||||
type: 'success',
|
||||
@ -70,7 +71,7 @@ export const EditContext = () => {
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useEnvironmentForm from '../hooks/useEnvironmentForm';
|
||||
import EnvironmentForm from '../EnvironmentForm/EnvironmentForm';
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
@ -19,7 +19,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
const CreateEnvironment = () => {
|
||||
const { setToastApiError, setToastData } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { environments } = useEnvironments();
|
||||
const canCreateMoreEnvs = environments.length < 7;
|
||||
const { createEnvironment, loading } = useEnvironmentApi();
|
||||
@ -49,7 +49,7 @@ const CreateEnvironment = () => {
|
||||
type: 'success',
|
||||
confetti: true,
|
||||
});
|
||||
history.push('/environments');
|
||||
navigate('/environments');
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
@ -66,7 +66,7 @@ const CreateEnvironment = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -5,21 +5,21 @@ import useEnvironment from 'hooks/api/getters/useEnvironment/useEnvironment';
|
||||
import useProjectRolePermissions from 'hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import EnvironmentForm from '../EnvironmentForm/EnvironmentForm';
|
||||
import useEnvironmentForm from '../hooks/useEnvironmentForm';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const EditEnvironment = () => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const id = useRequiredPathParam('id');
|
||||
const { environment } = useEnvironment(id);
|
||||
const { updateEnvironment } = useEnvironmentApi();
|
||||
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { name, type, setName, setType, errors, clearErrors } =
|
||||
useEnvironmentForm(environment.name, environment.type);
|
||||
const { refetch } = useProjectRolePermissions();
|
||||
@ -45,7 +45,7 @@ const EditEnvironment = () => {
|
||||
try {
|
||||
await updateEnvironment(id, editPayload());
|
||||
refetch();
|
||||
history.push('/environments');
|
||||
navigate('/environments');
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Successfully updated environment.',
|
||||
@ -56,7 +56,7 @@ const EditEnvironment = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { List } from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import useToast from 'hooks/useToast';
|
||||
@ -36,7 +36,7 @@ const EnvironmentList = () => {
|
||||
const [toggleDialog, setToggleDialog] = useState(false);
|
||||
const [confirmName, setConfirmName] = useState('');
|
||||
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { setToastApiError, setToastData } = useToast();
|
||||
const {
|
||||
deleteEnvironment,
|
||||
@ -155,7 +155,7 @@ const EnvironmentList = () => {
|
||||
));
|
||||
|
||||
const navigateToCreateEnvironment = () => {
|
||||
history.push('/environments/create');
|
||||
navigate('/environments/create');
|
||||
};
|
||||
return (
|
||||
<PageContent
|
||||
|
||||
@ -25,7 +25,7 @@ import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd';
|
||||
import { XYCoord, Identifier } from 'dnd-core';
|
||||
import DisabledIndicator from 'component/common/DisabledIndicator/DisabledIndicator';
|
||||
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface IEnvironmentListItemProps {
|
||||
env: IEnvironment;
|
||||
@ -56,7 +56,7 @@ const EnvironmentListItem = ({
|
||||
moveListItemApi,
|
||||
setToggleDialog,
|
||||
}: IEnvironmentListItemProps) => {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const ref = useRef<HTMLLIElement>(null);
|
||||
const ACCEPT_TYPE = 'LIST_ITEM';
|
||||
const [{ isDragging }, drag] = useDrag({
|
||||
@ -182,7 +182,7 @@ const EnvironmentListItem = ({
|
||||
<IconButton
|
||||
disabled={env.protected}
|
||||
onClick={() => {
|
||||
history.push(`/environments/${env.name}`);
|
||||
navigate(`/environments/${env.name}`);
|
||||
}}
|
||||
size="large"
|
||||
>
|
||||
|
||||
@ -5,13 +5,14 @@ import {
|
||||
FormEventHandler,
|
||||
ChangeEventHandler,
|
||||
} from 'react';
|
||||
import { Link, useHistory, useParams } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
TextField,
|
||||
Switch,
|
||||
Paper,
|
||||
FormControlLabel,
|
||||
Alert,
|
||||
} from '@mui/material';
|
||||
import { FileCopy } from '@mui/icons-material';
|
||||
import { styles as themeStyles } from 'component/common';
|
||||
@ -19,10 +20,10 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import styles from './CopyFeature.module.scss';
|
||||
import { trim } from 'component/common/util';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Alert } from '@mui/material';
|
||||
import { getTogglePath } from 'utils/routePathHelpers';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const CopyFeatureToggle = () => {
|
||||
const [replaceGroupId, setReplaceGroupId] = useState(true);
|
||||
@ -31,12 +32,10 @@ export const CopyFeatureToggle = () => {
|
||||
const [newToggleName, setNewToggleName] = useState<string>();
|
||||
const { cloneFeatureToggle, validateFeatureToggleName } = useFeatureApi();
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
const { name: copyToggleName, id: projectId } = useParams<{
|
||||
name: string;
|
||||
id: string;
|
||||
}>();
|
||||
const { feature } = useFeature(projectId, copyToggleName);
|
||||
const history = useHistory();
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
inputRef.current?.focus();
|
||||
@ -69,11 +68,11 @@ export const CopyFeatureToggle = () => {
|
||||
}
|
||||
|
||||
try {
|
||||
await cloneFeatureToggle(projectId, copyToggleName, {
|
||||
await cloneFeatureToggle(projectId, featureId, {
|
||||
name: newToggleName as string,
|
||||
replaceGroupId,
|
||||
});
|
||||
history.push(getTogglePath(projectId, newToggleName as string));
|
||||
navigate(getTogglePath(projectId, newToggleName as string));
|
||||
} catch (error) {
|
||||
setApiError(formatUnknownError(error));
|
||||
}
|
||||
@ -87,7 +86,7 @@ export const CopyFeatureToggle = () => {
|
||||
style={{ overflow: 'visible' }}
|
||||
>
|
||||
<div className={styles.header}>
|
||||
<h1>Copy {copyToggleName}</h1>
|
||||
<h1>Copy {featureId}</h1>
|
||||
</div>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(apiError)}
|
||||
@ -97,8 +96,8 @@ export const CopyFeatureToggle = () => {
|
||||
<p className={styles.text}>
|
||||
You are about to create a new feature toggle by cloning the
|
||||
configuration of feature toggle
|
||||
<Link to={getTogglePath(projectId, copyToggleName)}>
|
||||
{copyToggleName}
|
||||
<Link to={getTogglePath(projectId, featureId)}>
|
||||
{featureId}
|
||||
</Link>
|
||||
. You must give the new feature toggle a unique name before
|
||||
you can proceed.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import FeatureForm from '../FeatureForm/FeatureForm';
|
||||
import useFeatureForm from '../hooks/useFeatureForm';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
@ -16,7 +16,7 @@ const CreateFeature = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { setShowFeedback } = useContext(UIContext);
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
type,
|
||||
@ -46,7 +46,7 @@ const CreateFeature = () => {
|
||||
const payload = getTogglePayload();
|
||||
try {
|
||||
await createFeatureToggle(project, payload);
|
||||
history.push(`/projects/${project}/features/${name}`);
|
||||
navigate(`/projects/${project}/features/${name}`);
|
||||
setToastData({
|
||||
title: 'Toggle created successfully',
|
||||
text: 'Now you can start using your toggle.',
|
||||
@ -70,7 +70,7 @@ const CreateFeature = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import FeatureForm from '../FeatureForm/FeatureForm';
|
||||
import useFeatureForm from '../hooks/useFeatureForm';
|
||||
import * as jsonpatch from 'fast-json-patch';
|
||||
@ -9,14 +9,15 @@ import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const EditFeature = () => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const navigate = useNavigate();
|
||||
const { patchFeatureToggle, loading } = useFeatureApi();
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
|
||||
@ -53,7 +54,7 @@ const EditFeature = () => {
|
||||
const patch = createPatch();
|
||||
try {
|
||||
await patchFeatureToggle(project, featureId, patch);
|
||||
history.push(`/projects/${project}/features/${name}`);
|
||||
navigate(`/projects/${project}/features/${name}`);
|
||||
setToastData({
|
||||
title: 'Toggle updated successfully',
|
||||
type: 'success',
|
||||
@ -73,7 +74,7 @@ const EditFeature = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -16,7 +16,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
||||
import { trim } from 'component/common/util';
|
||||
import Input from 'component/common/Input/Input';
|
||||
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import React from 'react';
|
||||
import { useAuthPermissions } from 'hooks/api/getters/useAuth/useAuthPermissions';
|
||||
|
||||
@ -60,7 +60,7 @@ const FeatureForm: React.FC<IFeatureToggleForm> = ({
|
||||
}) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { featureTypes } = useFeatureTypes();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { permissions } = useAuthPermissions();
|
||||
const editable = mode !== 'Edit';
|
||||
|
||||
@ -116,7 +116,9 @@ const FeatureForm: React.FC<IFeatureToggleForm> = ({
|
||||
value={project}
|
||||
onChange={projectId => {
|
||||
setProject(projectId);
|
||||
history.replace(`/projects/${projectId}/create-toggle`);
|
||||
navigate(`/projects/${projectId}/create-toggle`, {
|
||||
replace: true,
|
||||
});
|
||||
}}
|
||||
enabled={editable}
|
||||
filter={projectFilterGenerator(permissions, CREATE_FEATURE)}
|
||||
|
||||
@ -6,7 +6,7 @@ import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import {
|
||||
@ -38,7 +38,7 @@ export const FeatureStrategyCreate = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { unleashUrl } = uiConfig;
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { feature, refetchFeature } = useFeatureImmutable(
|
||||
projectId,
|
||||
@ -72,7 +72,7 @@ export const FeatureStrategyCreate = () => {
|
||||
confetti: true,
|
||||
});
|
||||
refetchFeature();
|
||||
push(formatFeaturePath(projectId, featureId));
|
||||
navigate(formatFeaturePath(projectId, featureId));
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { IFeatureStrategy, IFeatureStrategyPayload } from 'interfaces/strategy';
|
||||
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
||||
@ -29,7 +29,7 @@ export const FeatureStrategyEdit = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { unleashUrl } = uiConfig;
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { feature, refetchFeature } = useFeatureImmutable(
|
||||
projectId,
|
||||
@ -77,7 +77,7 @@ export const FeatureStrategyEdit = () => {
|
||||
confetti: true,
|
||||
});
|
||||
refetchFeature();
|
||||
push(formatFeaturePath(projectId, featureId));
|
||||
navigate(formatFeaturePath(projectId, featureId));
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
import { IFeatureToggle } from 'interfaces/featureToggle';
|
||||
import { useStyles } from './FeatureStrategyForm.styles';
|
||||
import { formatFeaturePath } from '../FeatureStrategyEdit/FeatureStrategyEdit';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { STRATEGY_FORM_SUBMIT_ID } from 'utils/testIds';
|
||||
@ -51,7 +51,7 @@ export const FeatureStrategyForm = ({
|
||||
const hasValidConstraints = useConstraintsValidation(strategy.constraints);
|
||||
const enableProdGuard = useFeatureStrategyProdGuard(feature, environmentId);
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
uiConfig,
|
||||
@ -60,7 +60,7 @@ export const FeatureStrategyForm = ({
|
||||
} = useUiConfig();
|
||||
|
||||
const onCancel = () => {
|
||||
push(formatFeaturePath(feature.project, feature.name));
|
||||
navigate(formatFeaturePath(feature.project, feature.name));
|
||||
};
|
||||
|
||||
const onSubmitOrProdGuard = async (event: React.FormEvent) => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatFeaturePath } from '../FeatureStrategyEdit/FeatureStrategyEdit';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
@ -35,7 +35,7 @@ export const FeatureStrategyRemove = ({
|
||||
const { deleteStrategyFromFeature } = useFeatureStrategyApi();
|
||||
const { refetchFeature } = useFeature(projectId, featureId);
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onRemove = async (event: React.FormEvent) => {
|
||||
try {
|
||||
@ -51,7 +51,7 @@ export const FeatureStrategyRemove = ({
|
||||
type: 'success',
|
||||
});
|
||||
refetchFeature();
|
||||
push(formatFeaturePath(projectId, featureId));
|
||||
navigate(formatFeaturePath(projectId, featureId));
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { useParams } from 'react-router';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { useStyles } from './FeatureLog.styles';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { FeatureEventHistory } from 'component/history/FeatureEventHistory/FeatureEventHistory';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const FeatureLog = () => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { classes: styles } = useStyles();
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
|
||||
if (!feature.name) {
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { useParams } from 'react-router';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { useFeatureMetricsRaw } from 'hooks/api/getters/useFeatureMetricsRaw/useFeatureMetricsRaw';
|
||||
import PageContent from 'component/common/PageContent';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
@ -17,9 +15,11 @@ import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './FeatureMetrics.styles';
|
||||
import { usePageTitle } from 'hooks/usePageTitle';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const FeatureMetrics = () => {
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const environments = useFeatureMetricsEnvironments(projectId, featureId);
|
||||
const applications = useFeatureMetricsApplications(featureId);
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import React from 'react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getCreateTogglePath } from 'utils/routePathHelpers';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useStyles } from 'component/feature/FeatureView/FeatureNotFound/FeatureNotFound.styles';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { useFeaturesArchive } from 'hooks/api/getters/useFeaturesArchive/useFeaturesArchive';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const FeatureNotFound = () => {
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { archivedFeatures } = useFeaturesArchive();
|
||||
const { classes: styles } = useStyles();
|
||||
const { uiConfig } = useUiConfig();
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { DialogContentText } from '@mui/material';
|
||||
import { useParams } from 'react-router';
|
||||
import React, { useState } from 'react';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import Input from 'component/common/Input/Input';
|
||||
import { useStyles } from './AddTagDialog.styles';
|
||||
@ -11,6 +9,7 @@ import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import useTags from 'hooks/api/getters/useTags/useTags';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IAddTagDialogProps {
|
||||
open: boolean;
|
||||
@ -27,7 +26,7 @@ interface IDefaultTag {
|
||||
const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => {
|
||||
const DEFAULT_TAG: IDefaultTag = { type: 'simple', value: '' };
|
||||
const { classes: styles } = useStyles();
|
||||
const { featureId } = useParams<IFeatureViewParams>();
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { addTagToFeature, loading } = useFeatureApi();
|
||||
const { refetch } = useTags(featureId);
|
||||
const [errors, setErrors] = useState({ tagError: '' });
|
||||
|
||||
@ -2,7 +2,7 @@ import FeatureOverviewMetaData from './FeatureOverviewMetaData/FeatureOverviewMe
|
||||
import { useStyles } from './FeatureOverview.styles';
|
||||
import FeatureOverviewEnvironments from './FeatureOverviewEnvironments/FeatureOverviewEnvironments';
|
||||
import FeatureOverviewEnvSwitches from './FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitches';
|
||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
||||
import { Routes, Route, useNavigate } from 'react-router-dom';
|
||||
import { FeatureStrategyCreate } from 'component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate';
|
||||
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
||||
import {
|
||||
@ -14,11 +14,11 @@ import { usePageTitle } from 'hooks/usePageTitle';
|
||||
|
||||
const FeatureOverview = () => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const featurePath = formatFeaturePath(projectId, featureId);
|
||||
const onSidebarClose = () => push(featurePath);
|
||||
const onSidebarClose = () => navigate(featurePath);
|
||||
usePageTitle(featureId);
|
||||
|
||||
return (
|
||||
@ -30,26 +30,32 @@ const FeatureOverview = () => {
|
||||
<div className={styles.mainContent}>
|
||||
<FeatureOverviewEnvironments />
|
||||
</div>
|
||||
<Switch>
|
||||
<Route path="/projects/:projectId/features/:featureId/strategies/create">
|
||||
<SidebarModal
|
||||
label="Create feature strategy"
|
||||
onClose={onSidebarClose}
|
||||
open
|
||||
>
|
||||
<FeatureStrategyCreate />
|
||||
</SidebarModal>
|
||||
</Route>
|
||||
<Route path="/projects/:projectId/features/:featureId/strategies/edit">
|
||||
<SidebarModal
|
||||
label="Edit feature strategy"
|
||||
onClose={onSidebarClose}
|
||||
open
|
||||
>
|
||||
<FeatureStrategyEdit />
|
||||
</SidebarModal>
|
||||
</Route>
|
||||
</Switch>
|
||||
<Routes>
|
||||
<Route
|
||||
path="strategies/create"
|
||||
element={
|
||||
<SidebarModal
|
||||
label="Create feature strategy"
|
||||
onClose={onSidebarClose}
|
||||
open
|
||||
>
|
||||
<FeatureStrategyCreate />
|
||||
</SidebarModal>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="strategies/edit"
|
||||
element={
|
||||
<SidebarModal
|
||||
label="Edit feature strategy"
|
||||
onClose={onSidebarClose}
|
||||
open
|
||||
>
|
||||
<FeatureStrategyEdit />
|
||||
</SidebarModal>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import { useParams } from 'react-router';
|
||||
import { ENVIRONMENT_STRATEGY_ERROR } from 'constants/apiErrors';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
|
||||
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||
import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
|
||||
import React from 'react';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useStyles } from './FeatureOverviewEnvSwitch.styles';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IFeatureOverviewEnvSwitchProps {
|
||||
env: IFeatureEnvironment;
|
||||
@ -25,7 +24,8 @@ const FeatureOverviewEnvSwitch = ({
|
||||
text,
|
||||
showInfoBox,
|
||||
}: IFeatureOverviewEnvSwitchProps) => {
|
||||
const { featureId, projectId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } =
|
||||
useFeatureApi();
|
||||
const { refetchFeature } = useFeature(projectId, featureId);
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import EnvironmentStrategyDialog from 'component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog';
|
||||
import FeatureOverviewEnvSwitch from './FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch';
|
||||
import { useStyles } from './FeatureOverviewEnvSwitches.styles';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const FeatureOverviewEnvSwitches = () => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { featureId, projectId } = useParams<IFeatureViewParams>();
|
||||
useFeatureApi();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
useFeatureApi();
|
||||
|
||||
const [showInfoBox, setShowInfoBox] = useState(false);
|
||||
const [environmentName, setEnvironmentName] = useState('');
|
||||
|
||||
@ -6,11 +6,9 @@ import {
|
||||
} from '@mui/material';
|
||||
import { ExpandMore } from '@mui/icons-material';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import useFeatureMetrics from 'hooks/api/getters/useFeatureMetrics/useFeatureMetrics';
|
||||
import { IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { getFeatureMetrics } from 'utils/getFeatureMetrics';
|
||||
import {
|
||||
getFeatureStrategyIcon,
|
||||
@ -26,6 +24,7 @@ import FeatureOverviewEnvironmentFooter from './FeatureOverviewEnvironmentFooter
|
||||
import FeatureOverviewEnvironmentMetrics from './FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics';
|
||||
import { FeatureStrategyMenu } from 'component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu';
|
||||
import { FEATURE_ENVIRONMENT_ACCORDION } from 'utils/testIds';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IStrategyIconObject {
|
||||
count: number;
|
||||
@ -41,7 +40,8 @@ const FeatureOverviewEnvironment = ({
|
||||
env,
|
||||
}: IFeatureOverviewEnvironmentProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { metrics } = useFeatureMetrics(projectId, featureId);
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import FeatureOverviewEnvironmentStrategies from '../FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies';
|
||||
import { useStyles } from '../FeatureOverviewEnvironment.styles';
|
||||
import { IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import { FeatureStrategyEmpty } from 'component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IFeatureOverviewEnvironmentBodyProps {
|
||||
getOverviewText: () => string;
|
||||
@ -15,7 +14,8 @@ const FeatureOverviewEnvironmentBody = ({
|
||||
featureEnvironment,
|
||||
getOverviewText,
|
||||
}: IFeatureOverviewEnvironmentBodyProps) => {
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
if (!featureEnvironment) {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Edit } from '@mui/icons-material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import {
|
||||
getFeatureStrategyIcon,
|
||||
@ -14,6 +13,7 @@ import { useStyles } from './FeatureOverviewEnvironmentStrategy.styles';
|
||||
import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
|
||||
import { FeatureStrategyRemove } from 'component/feature/FeatureStrategy/FeatureStrategyRemove/FeatureStrategyRemove';
|
||||
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IFeatureOverviewEnvironmentStrategyProps {
|
||||
environmentId: string;
|
||||
@ -24,7 +24,8 @@ const FeatureOverviewEnvironmentStrategy = ({
|
||||
environmentId,
|
||||
strategy,
|
||||
}: IFeatureOverviewEnvironmentStrategyProps) => {
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const theme = useTheme();
|
||||
const { classes: styles } = useStyles();
|
||||
const Icon = getFeatureStrategyIcon(strategy.name);
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
|
||||
import FeatureOverviewEnvironment from './FeatureOverviewEnvironment/FeatureOverviewEnvironment';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const FeatureOverviewEnvironments = () => {
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
|
||||
if (!feature) return null;
|
||||
|
||||
@ -1,26 +1,23 @@
|
||||
import { capitalize } from '@mui/material';
|
||||
import classnames from 'classnames';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './FeatureOverviewMetadata.styles';
|
||||
|
||||
import { Edit } from '@mui/icons-material';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
import useTags from 'hooks/api/getters/useTags/useTags';
|
||||
import FeatureOverviewTags from './FeatureOverviewTags/FeatureOverviewTags';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const FeatureOverviewMetaData = () => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { tags } = useTags(featureId);
|
||||
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
|
||||
const { project, description, type } = feature;
|
||||
|
||||
const IconComponent = getFeatureTypeIcons(type);
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { Chip } from '@mui/material';
|
||||
import { Close, Label } from '@mui/icons-material';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useTags from 'hooks/api/getters/useTags/useTags';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { useStyles } from './FeatureOverviewTags.styles';
|
||||
import slackIcon from 'assets/icons/slack.svg';
|
||||
import jiraIcon from 'assets/icons/jira.svg';
|
||||
@ -18,6 +16,7 @@ import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IFeatureOverviewTagsProps extends React.HTMLProps<HTMLButtonElement> {
|
||||
projectId: string;
|
||||
@ -33,7 +32,7 @@ const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({
|
||||
type: '',
|
||||
});
|
||||
const { classes: styles } = useStyles();
|
||||
const { featureId } = useParams<IFeatureViewParams>();
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { tags, refetch } = useTags(featureId);
|
||||
const { tagTypes } = useTagTypes();
|
||||
const { deleteTagFromFeature } = useFeatureApi();
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { DialogContentText } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
@ -8,6 +6,7 @@ import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import React from 'react';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IStaleDialogProps {
|
||||
open: boolean;
|
||||
@ -17,7 +16,8 @@ interface IStaleDialogProps {
|
||||
|
||||
const StaleDialog = ({ open, setOpen, stale }: IStaleDialogProps) => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { patchFeatureToggle } = useFeatureApi();
|
||||
const { refetchFeature } = useFeature(projectId, featureId);
|
||||
|
||||
|
||||
@ -4,16 +4,16 @@ import { useStyles } from './FeatureSettings.styles';
|
||||
import { List, ListItem } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import FeatureSettingsProject from './FeatureSettingsProject/FeatureSettingsProject';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { FeatureSettingsInformation } from './FeatureSettingsInformation/FeatureSettingsInformation';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const METADATA = 'metadata';
|
||||
const PROJECT = 'project';
|
||||
|
||||
export const FeatureSettings = () => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const [settings, setSettings] = useState(METADATA);
|
||||
|
||||
return (
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Typography } from '@mui/material';
|
||||
import { Edit } from '@mui/icons-material';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
@ -17,10 +17,10 @@ export const FeatureSettingsInformation = ({
|
||||
}: IFeatureSettingsInformationProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onEdit = () => {
|
||||
history.push(`/projects/${projectId}/features/${featureId}/edit`);
|
||||
navigate(`/projects/${projectId}/features/${featureId}/edit`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -3,19 +3,19 @@ import * as jsonpatch from 'fast-json-patch';
|
||||
import { TextField } from '@mui/material';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import FeatureTypeSelect from './FeatureTypeSelect/FeatureTypeSelect';
|
||||
import { useParams } from 'react-router';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import useToast from 'hooks/useToast';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const FeatureSettingsMetadata = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
||||
const [description, setDescription] = useState(feature.description);
|
||||
const [type, setType] = useState(feature.type);
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router';
|
||||
import { useNavigate } from 'react-router';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { MOVE_FEATURE_TOGGLE } from 'component/providers/AccessProvider/permissions';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
@ -13,10 +12,12 @@ import FeatureSettingsProjectConfirm from './FeatureSettingsProjectConfirm/Featu
|
||||
import { IPermission } from 'interfaces/user';
|
||||
import { useAuthPermissions } from 'hooks/api/getters/useAuth/useAuthPermissions';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const FeatureSettingsProject = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
||||
const [project, setProject] = useState(feature.project);
|
||||
const [dirty, setDirty] = useState(false);
|
||||
@ -25,7 +26,7 @@ const FeatureSettingsProject = () => {
|
||||
const { permissions = [] } = useAuthPermissions();
|
||||
const { changeFeatureProject } = useFeatureApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (project !== feature.project) {
|
||||
@ -59,9 +60,9 @@ const FeatureSettingsProject = () => {
|
||||
});
|
||||
setDirty(false);
|
||||
setShowConfirmDialog(false);
|
||||
history.replace(
|
||||
`/projects/${newProject}/features/${featureId}/settings`
|
||||
);
|
||||
navigate(`/projects/${newProject}/features/${featureId}/settings`, {
|
||||
replace: true,
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
|
||||
@ -15,8 +15,6 @@ import { modalStyles, trim } from 'component/common/util';
|
||||
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
|
||||
import { UPDATE_FEATURE_VARIANTS } from 'component/providers/AccessProvider/permissions';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { IFeatureVariant } from 'interfaces/featureToggle';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
|
||||
@ -26,6 +24,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||
import { useOverrides } from './useOverrides';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const payloadOptions = [
|
||||
{ key: 'string', label: 'string' },
|
||||
@ -62,7 +61,8 @@ export const AddVariant = ({
|
||||
const [overrides, overridesDispatch] = useOverrides([]);
|
||||
const [error, setError] = useState<Record<string, string>>({});
|
||||
const { classes: themeStyles } = useThemeStyles();
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
const [variants, setVariants] = useState<IFeatureVariant[]>([]);
|
||||
const { context } = useUnleashContext();
|
||||
|
||||
@ -14,8 +14,6 @@ import { AddVariant } from './AddFeatureVariant/AddFeatureVariant';
|
||||
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { useParams } from 'react-router';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import FeatureVariantListItem from './FeatureVariantsListItem/FeatureVariantsListItem';
|
||||
import { UPDATE_FEATURE_VARIANTS } from 'component/providers/AccessProvider/permissions';
|
||||
@ -30,10 +28,12 @@ import cloneDeep from 'lodash.clonedeep';
|
||||
import useDeleteVariantMarkup from './FeatureVariantsListItem/useDeleteVariantMarkup';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const FeatureOverviewVariants = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
||||
const [variants, setVariants] = useState<IFeatureVariant[]>([]);
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
@ -1,12 +1,17 @@
|
||||
import { Tab, Tabs, useMediaQuery } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import { Archive, FileCopy, Label, WatchLater } from '@mui/icons-material';
|
||||
import { Link, Route, useHistory, useParams, Switch } from 'react-router-dom';
|
||||
import {
|
||||
Link,
|
||||
Route,
|
||||
useNavigate,
|
||||
Routes,
|
||||
useLocation,
|
||||
} from 'react-router-dom';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import {
|
||||
CREATE_FEATURE,
|
||||
DELETE_FEATURE,
|
||||
@ -27,9 +32,12 @@ import AddTagDialog from './FeatureOverview/AddTagDialog/AddTagDialog';
|
||||
import StatusChip from 'component/common/StatusChip/StatusChip';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { FeatureNotFound } from 'component/feature/FeatureView/FeatureNotFound/FeatureNotFound';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const FeatureView = () => {
|
||||
const { projectId, featureId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
|
||||
const { refetch: projectRefetch } = useProject(projectId);
|
||||
const [openTagDialog, setOpenTagDialog] = useState(false);
|
||||
const { archiveFeatureToggle } = useFeatureApi();
|
||||
@ -44,7 +52,8 @@ export const FeatureView = () => {
|
||||
);
|
||||
|
||||
const { classes: styles } = useStyles();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
const ref = useLoading(loading);
|
||||
|
||||
const basePath = `/projects/${projectId}/features/${featureId}`;
|
||||
@ -59,7 +68,7 @@ export const FeatureView = () => {
|
||||
});
|
||||
setShowDelDialog(false);
|
||||
projectRefetch();
|
||||
history.push(`/projects/${projectId}`);
|
||||
navigate(`/projects/${projectId}`);
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
setShowDelDialog(false);
|
||||
@ -88,9 +97,7 @@ export const FeatureView = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const activeTab =
|
||||
tabData.find(tab => tab.path === history.location.pathname) ??
|
||||
tabData[0];
|
||||
const activeTab = tabData.find(tab => tab.path === pathname) ?? tabData[0];
|
||||
|
||||
const renderTabs = () => {
|
||||
return tabData.map((tab, index) => {
|
||||
@ -100,7 +107,7 @@ export const FeatureView = () => {
|
||||
key={tab.title}
|
||||
label={tab.title}
|
||||
value={tab.path}
|
||||
onClick={() => history.push(tab.path)}
|
||||
onClick={() => navigate(tab.path)}
|
||||
className={styles.tabButton}
|
||||
/>
|
||||
);
|
||||
@ -182,28 +189,13 @@ export const FeatureView = () => {
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
<Switch>
|
||||
<Route
|
||||
path={`/projects/:projectId/features/:featureId/metrics`}
|
||||
component={FeatureMetrics}
|
||||
/>
|
||||
<Route
|
||||
path={`/projects/:projectId/features/:featureId/logs`}
|
||||
component={FeatureLog}
|
||||
/>
|
||||
<Route
|
||||
path={`/projects/:projectId/features/:featureId/variants`}
|
||||
component={FeatureVariants}
|
||||
/>
|
||||
<Route
|
||||
path={`/projects/:projectId/features/:featureId/settings`}
|
||||
component={FeatureSettings}
|
||||
/>
|
||||
<Route
|
||||
path={`/projects/:projectId/features/:featureId`}
|
||||
component={FeatureOverview}
|
||||
/>
|
||||
</Switch>
|
||||
<Routes>
|
||||
<Route path="metrics" element={<FeatureMetrics />} />
|
||||
<Route path="logs" element={<FeatureLog />} />
|
||||
<Route path="variants" element={<FeatureVariants />} />
|
||||
<Route path="settings" element={<FeatureSettings />} />
|
||||
<Route path="*" element={<FeatureOverview />} />
|
||||
</Routes>
|
||||
<Dialogue
|
||||
onClick={() => archiveToggle()}
|
||||
open={showDelDialog}
|
||||
|
||||
@ -1,33 +1,31 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Redirect, useParams } from 'react-router-dom';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures';
|
||||
import { getTogglePath } from 'utils/routePathHelpers';
|
||||
import { FeatureSchema } from 'openapi';
|
||||
|
||||
interface IRedirectParams {
|
||||
name: string;
|
||||
}
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const RedirectFeatureView = () => {
|
||||
const { name } = useParams<IRedirectParams>();
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { features = [] } = useFeatures();
|
||||
const [featureToggle, setFeatureToggle] = useState<FeatureSchema>();
|
||||
|
||||
useEffect(() => {
|
||||
const toggle = features.find(
|
||||
(toggle: FeatureSchema) => toggle.name === name
|
||||
(toggle: FeatureSchema) => toggle.name === featureId
|
||||
);
|
||||
|
||||
setFeatureToggle(toggle);
|
||||
}, [features, name]);
|
||||
}, [features, featureId]);
|
||||
|
||||
if (!featureToggle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Redirect
|
||||
<Navigate
|
||||
to={getTogglePath(featureToggle?.project, featureToggle?.name)}
|
||||
replace
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import useQueryParams from 'hooks/useQueryParams';
|
||||
import { IFeatureViewParams } from 'interfaces/params';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const useFeatureForm = (
|
||||
initialName = '',
|
||||
@ -11,7 +10,7 @@ const useFeatureForm = (
|
||||
initialDescription = '',
|
||||
initialImpressionData = false
|
||||
) => {
|
||||
const { projectId } = useParams<IFeatureViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const params = useQueryParams();
|
||||
const { validateFeatureToggleName } = useFeatureApi();
|
||||
const toggleQueryName = params.get('name');
|
||||
|
||||
@ -3,7 +3,7 @@ import { makeStyles } from 'tss-react/mui';
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
historyItem: {
|
||||
padding: '5px',
|
||||
'&:nth-child(odd)': {
|
||||
'&:nth-of-type(odd)': {
|
||||
backgroundColor: theme.code.background,
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { FeatureEventHistory } from '../FeatureEventHistory/FeatureEventHistory';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const FeatureEventHistoryPage = () => {
|
||||
const { toggleName } = useParams<{ toggleName: string }>();
|
||||
const toggleName = useRequiredPathParam('toggleName');
|
||||
|
||||
return <FeatureEventHistory toggleName={toggleName} />;
|
||||
};
|
||||
|
||||
@ -21,41 +21,17 @@ export const LayoutPicker = ({ children }: ILayoutPickerProps) => {
|
||||
};
|
||||
|
||||
const isStandalonePage = (pathname: string): boolean => {
|
||||
const isLoginPage = matchPath(pathname, {
|
||||
path: '/login',
|
||||
return standalonePagePatterns.some(pattern => {
|
||||
return matchPath(pattern, pathname);
|
||||
});
|
||||
|
||||
const isNewUserPage = matchPath(pathname, {
|
||||
path: '/new-user',
|
||||
});
|
||||
|
||||
const isChangePasswordPage = matchPath(pathname, {
|
||||
path: '/reset-password',
|
||||
});
|
||||
|
||||
const isResetPasswordSuccessPage = matchPath(pathname, {
|
||||
path: '/reset-password-success',
|
||||
});
|
||||
|
||||
const isForgottenPasswordPage = matchPath(pathname, {
|
||||
path: '/forgotten-password',
|
||||
});
|
||||
|
||||
const isSplashPage = matchPath(pathname, {
|
||||
path: '/splash/:id',
|
||||
});
|
||||
|
||||
const is404 = matchPath(pathname, {
|
||||
path: '/404',
|
||||
});
|
||||
|
||||
return Boolean(
|
||||
isLoginPage ||
|
||||
isNewUserPage ||
|
||||
isChangePasswordPage ||
|
||||
isResetPasswordSuccessPage ||
|
||||
isForgottenPasswordPage ||
|
||||
isSplashPage ||
|
||||
is404
|
||||
);
|
||||
};
|
||||
|
||||
const standalonePagePatterns = [
|
||||
'/login',
|
||||
'/new-user',
|
||||
'/reset-password',
|
||||
'/reset-password-success',
|
||||
'/forgotten-password',
|
||||
'/splash/:splashId',
|
||||
'/404',
|
||||
];
|
||||
|
||||
@ -21,23 +21,23 @@ Array [
|
||||
"component": [Function],
|
||||
"menu": Object {},
|
||||
"parent": "/projects",
|
||||
"path": "/projects/:id/edit",
|
||||
"title": ":id",
|
||||
"path": "/projects/:projectId/edit",
|
||||
"title": ":projectId",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"menu": Object {},
|
||||
"parent": "/archive",
|
||||
"path": "/projects/:id/archived",
|
||||
"title": ":name",
|
||||
"path": "/projects/:projectId/archived",
|
||||
"title": ":projectId",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"menu": Object {},
|
||||
"parent": "/projects/:id/features/:name/:activeTab",
|
||||
"path": "/projects/:id/features/:name/:activeTab/copy",
|
||||
"parent": "/projects/:projectId/features/:featureId/:activeTab",
|
||||
"path": "/projects/:projectId/features/:featureId/:activeTab/copy",
|
||||
"title": "Copy",
|
||||
"type": "protected",
|
||||
},
|
||||
@ -53,22 +53,14 @@ Array [
|
||||
"component": [Function],
|
||||
"menu": Object {},
|
||||
"parent": "/projects",
|
||||
"path": "/projects/:projectId/features/:featureId",
|
||||
"path": "/projects/:projectId/features/:featureId/*",
|
||||
"title": "FeatureView",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"menu": Object {},
|
||||
"parent": "/projects",
|
||||
"path": "/projects/:id/features/:name/:activeTab",
|
||||
"title": ":name",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"menu": Object {},
|
||||
"parent": "/projects/:id/features",
|
||||
"parent": "/projects/:projectId/features",
|
||||
"path": "/projects/:projectId/create-toggle",
|
||||
"title": "Create feature toggle",
|
||||
"type": "protected",
|
||||
@ -77,8 +69,8 @@ Array [
|
||||
"component": [Function],
|
||||
"menu": Object {},
|
||||
"parent": "/features",
|
||||
"path": "/projects/:projectId/features2/:name",
|
||||
"title": ":name",
|
||||
"path": "/projects/:projectId/features2/:featureId",
|
||||
"title": ":featureId",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
@ -86,8 +78,8 @@ Array [
|
||||
"flag": "P",
|
||||
"menu": Object {},
|
||||
"parent": "/projects",
|
||||
"path": "/projects/:id/:activeTab",
|
||||
"title": ":id",
|
||||
"path": "/projects/:projectId/:activeTab",
|
||||
"title": ":projectId",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
@ -95,8 +87,8 @@ Array [
|
||||
"flag": "P",
|
||||
"menu": Object {},
|
||||
"parent": "/projects",
|
||||
"path": "/projects/:id",
|
||||
"title": ":id",
|
||||
"path": "/projects/:projectId",
|
||||
"title": ":projectId",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
@ -112,8 +104,8 @@ Array [
|
||||
"component": [Function],
|
||||
"menu": Object {},
|
||||
"parent": "/features",
|
||||
"path": "/features/:activeTab/:name",
|
||||
"title": ":name",
|
||||
"path": "/features/:activeTab/:featureId",
|
||||
"title": ":featureId",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
|
||||
@ -72,24 +72,24 @@ export const routes: IRoute[] = [
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:id/edit',
|
||||
path: '/projects/:projectId/edit',
|
||||
parent: '/projects',
|
||||
title: ':id',
|
||||
title: ':projectId',
|
||||
component: EditProject,
|
||||
type: 'protected',
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:id/archived',
|
||||
title: ':name',
|
||||
path: '/projects/:projectId/archived',
|
||||
title: ':projectId',
|
||||
parent: '/archive',
|
||||
component: RedirectArchive,
|
||||
type: 'protected',
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:id/features/:name/:activeTab/copy',
|
||||
parent: '/projects/:id/features/:name/:activeTab',
|
||||
path: '/projects/:projectId/features/:featureId/:activeTab/copy',
|
||||
parent: '/projects/:projectId/features/:featureId/:activeTab',
|
||||
title: 'Copy',
|
||||
component: CopyFeatureToggle,
|
||||
type: 'protected',
|
||||
@ -104,50 +104,42 @@ export const routes: IRoute[] = [
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/features/:featureId',
|
||||
path: '/projects/:projectId/features/:featureId/*',
|
||||
parent: '/projects',
|
||||
title: 'FeatureView',
|
||||
component: FeatureView,
|
||||
type: 'protected',
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:id/features/:name/:activeTab',
|
||||
parent: '/projects',
|
||||
title: ':name',
|
||||
component: FeatureView,
|
||||
type: 'protected',
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/create-toggle',
|
||||
parent: '/projects/:id/features',
|
||||
parent: '/projects/:projectId/features',
|
||||
title: 'Create feature toggle',
|
||||
component: CreateFeature,
|
||||
type: 'protected',
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:projectId/features2/:name',
|
||||
path: '/projects/:projectId/features2/:featureId',
|
||||
parent: '/features',
|
||||
title: ':name',
|
||||
title: ':featureId',
|
||||
component: RedirectFeatureView,
|
||||
type: 'protected',
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:id/:activeTab',
|
||||
path: '/projects/:projectId/:activeTab',
|
||||
parent: '/projects',
|
||||
title: ':id',
|
||||
title: ':projectId',
|
||||
component: Project,
|
||||
flag: P,
|
||||
type: 'protected',
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/projects/:id',
|
||||
path: '/projects/:projectId',
|
||||
parent: '/projects',
|
||||
title: ':id',
|
||||
title: ':projectId',
|
||||
component: Project,
|
||||
flag: P,
|
||||
type: 'protected',
|
||||
@ -163,9 +155,9 @@ export const routes: IRoute[] = [
|
||||
|
||||
// Features
|
||||
{
|
||||
path: '/features/:activeTab/:name',
|
||||
path: '/features/:activeTab/:featureId',
|
||||
parent: '/features',
|
||||
title: ':name',
|
||||
title: ':featureId',
|
||||
component: RedirectFeatureView,
|
||||
type: 'protected',
|
||||
menu: {},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import ProjectForm from '../ProjectForm/ProjectForm';
|
||||
import useProjectForm from '../hooks/useProjectForm';
|
||||
import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||
@ -14,7 +14,7 @@ const CreateProject = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { refetchUser } = useAuthUser();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
projectId,
|
||||
projectName,
|
||||
@ -42,7 +42,7 @@ const CreateProject = () => {
|
||||
try {
|
||||
await createProject(payload);
|
||||
refetchUser();
|
||||
history.push(`/projects/${projectId}`);
|
||||
navigate(`/projects/${projectId}`);
|
||||
setToastData({
|
||||
title: 'Project created',
|
||||
text: 'Now you can add toggles to this project',
|
||||
@ -65,7 +65,7 @@ const CreateProject = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import ProjectForm from '../ProjectForm/ProjectForm';
|
||||
import useProjectForm from '../hooks/useProjectForm';
|
||||
import { UpdateButton } from 'component/common/UpdateButton/UpdateButton';
|
||||
@ -9,13 +9,14 @@ import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const EditProject = () => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const id = useRequiredPathParam('projectId');
|
||||
const { project } = useProject(id);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
projectId,
|
||||
projectName,
|
||||
@ -52,7 +53,7 @@ const EditProject = () => {
|
||||
try {
|
||||
await editProject(id, payload);
|
||||
refetch();
|
||||
history.push(`/projects/${id}`);
|
||||
navigate(`/projects/${id}`);
|
||||
setToastData({
|
||||
title: 'Project information updated',
|
||||
type: 'success',
|
||||
@ -64,7 +65,7 @@ const EditProject = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory, useParams } from 'react-router';
|
||||
import { useNavigate } from 'react-router';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import useLoading from 'hooks/useLoading';
|
||||
import ApiError from 'component/common/ApiError/ApiError';
|
||||
@ -17,27 +17,30 @@ import ProjectHealth from './ProjectHealth/ProjectHealth';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
||||
import { TabPanel } from 'component/common/TabNav/TabPanel/TabPanel';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useOptionalPathParam } from 'hooks/useOptionalPathParam';
|
||||
|
||||
const Project = () => {
|
||||
const { id, activeTab } = useParams<{ id: string; activeTab: string }>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const activeTab = useOptionalPathParam('activeTab');
|
||||
const params = useQueryParams();
|
||||
const { project, error, loading, refetch } = useProject(id);
|
||||
const { project, error, loading, refetch } = useProject(projectId);
|
||||
const ref = useLoading(loading);
|
||||
const { setToastData } = useToast();
|
||||
const { classes: styles } = useStyles();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const basePath = `/projects/${id}`;
|
||||
const basePath = `/projects/${projectId}`;
|
||||
const tabData = [
|
||||
{
|
||||
title: 'Overview',
|
||||
component: <ProjectOverview projectId={id} />,
|
||||
component: <ProjectOverview projectId={projectId} />,
|
||||
path: basePath,
|
||||
name: 'overview',
|
||||
},
|
||||
{
|
||||
title: 'Health',
|
||||
component: <ProjectHealth projectId={id} />,
|
||||
component: <ProjectHealth projectId={projectId} />,
|
||||
path: `${basePath}/health`,
|
||||
name: 'health',
|
||||
},
|
||||
@ -49,13 +52,13 @@ const Project = () => {
|
||||
},
|
||||
{
|
||||
title: 'Environments',
|
||||
component: <ProjectEnvironment projectId={id} />,
|
||||
component: <ProjectEnvironment projectId={projectId} />,
|
||||
path: `${basePath}/environments`,
|
||||
name: 'environments',
|
||||
},
|
||||
{
|
||||
title: 'Archive',
|
||||
component: <ProjectFeaturesArchive projectId={id} />,
|
||||
component: <ProjectFeaturesArchive projectId={projectId} />,
|
||||
path: `${basePath}/archive`,
|
||||
name: 'archive',
|
||||
},
|
||||
@ -92,7 +95,7 @@ const Project = () => {
|
||||
id={`tab-${index}`}
|
||||
aria-controls={`tabpanel-${index}`}
|
||||
label={tab.title}
|
||||
onClick={() => history.push(tab.path)}
|
||||
onClick={() => navigate(tab.path)}
|
||||
className={styles.tabButton}
|
||||
/>
|
||||
);
|
||||
@ -122,7 +125,9 @@ const Project = () => {
|
||||
<PermissionIconButton
|
||||
permission={UPDATE_PROJECT}
|
||||
projectId={project?.id}
|
||||
onClick={() => history.push(`/projects/${id}/edit`)}
|
||||
onClick={() =>
|
||||
navigate(`/projects/${projectId}/edit`)
|
||||
}
|
||||
tooltip="Edit project"
|
||||
data-loading
|
||||
>
|
||||
|
||||
@ -2,8 +2,7 @@ import { useContext, useMemo, useState } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
import { useParams } from 'react-router';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { SearchField } from 'component/common/SearchField/SearchField';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
@ -18,6 +17,7 @@ import { useStyles } from './ProjectFeatureToggles.styles';
|
||||
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import classnames from 'classnames';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IProjectFeatureTogglesProps {
|
||||
features: IFeatureToggleListItem[];
|
||||
@ -29,8 +29,8 @@ export const ProjectFeatureToggles = ({
|
||||
loading,
|
||||
}: IProjectFeatureTogglesProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const history = useHistory();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const navigate = useNavigate();
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { uiConfig } = useUiConfig();
|
||||
const [filter, setFilter] = useState('');
|
||||
@ -76,16 +76,16 @@ export const ProjectFeatureToggles = ({
|
||||
|
||||
<ResponsiveButton
|
||||
onClick={() =>
|
||||
history.push(
|
||||
navigate(
|
||||
getCreateTogglePath(
|
||||
id,
|
||||
projectId,
|
||||
uiConfig.flags.E
|
||||
)
|
||||
)
|
||||
}
|
||||
maxWidth="700px"
|
||||
Icon={Add}
|
||||
projectId={id}
|
||||
projectId={projectId}
|
||||
permission={CREATE_FEATURE}
|
||||
className={styles.button}
|
||||
>
|
||||
@ -102,7 +102,7 @@ export const ProjectFeatureToggles = ({
|
||||
<FeatureToggleListNew
|
||||
features={filteredFeatures}
|
||||
loading={loading}
|
||||
projectId={id}
|
||||
projectId={projectId}
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
@ -111,11 +111,11 @@ export const ProjectFeatureToggles = ({
|
||||
No feature toggles added yet.
|
||||
</p>
|
||||
<ConditionallyRender
|
||||
condition={hasAccess(CREATE_FEATURE, id)}
|
||||
condition={hasAccess(CREATE_FEATURE, projectId)}
|
||||
show={
|
||||
<Link
|
||||
to={getCreateTogglePath(
|
||||
id,
|
||||
projectId,
|
||||
uiConfig.flags.E
|
||||
)}
|
||||
className={styles.link}
|
||||
|
||||
@ -5,8 +5,6 @@ import { ProjectAccessAddUser } from './ProjectAccessAddUser/ProjectAccessAddUse
|
||||
import PageContent from 'component/common/PageContent';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useStyles } from './ProjectAccess.styles';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { IProjectViewParams } from 'interfaces/params';
|
||||
import usePagination from 'hooks/usePagination';
|
||||
import PaginateUI from 'component/common/PaginateUI/PaginateUI';
|
||||
import useToast from 'hooks/useToast';
|
||||
@ -17,9 +15,10 @@ import useProjectAccess, {
|
||||
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { ProjectAccessList } from './ProjectAccessList/ProjectAccessList';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const ProjectAccess = () => {
|
||||
const { id: projectId } = useParams<IProjectViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { classes: styles } = useStyles();
|
||||
const { access, refetchProjectAccess } = useProjectAccess(projectId);
|
||||
const { setToastData } = useToast();
|
||||
|
||||
@ -6,32 +6,32 @@ import {
|
||||
Button,
|
||||
InputAdornment,
|
||||
SelectChangeEvent,
|
||||
Alert,
|
||||
} from '@mui/material';
|
||||
import { Search } from '@mui/icons-material';
|
||||
import Autocomplete from '@mui/material/Autocomplete';
|
||||
import { Alert } from '@mui/material';
|
||||
import { ProjectRoleSelect } from '../ProjectRoleSelect/ProjectRoleSelect';
|
||||
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import useToast from 'hooks/useToast';
|
||||
import useProjectAccess, {
|
||||
IProjectAccessUser,
|
||||
} from 'hooks/api/getters/useProjectAccess/useProjectAccess';
|
||||
import { IProjectRole } from 'interfaces/role';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IProjectAccessAddUserProps {
|
||||
roles: IProjectRole[];
|
||||
}
|
||||
|
||||
export const ProjectAccessAddUser = ({ roles }: IProjectAccessAddUserProps) => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const [user, setUser] = useState<IProjectAccessUser | undefined>();
|
||||
const [role, setRole] = useState<IProjectRole | undefined>();
|
||||
const [options, setOptions] = useState<IProjectAccessUser[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { setToastData } = useToast();
|
||||
const { refetchProjectAccess, access } = useProjectAccess(id);
|
||||
const { refetchProjectAccess, access } = useProjectAccess(projectId);
|
||||
|
||||
const { searchProjectUser, addUserToRole } = useProjectApi();
|
||||
|
||||
@ -114,7 +114,7 @@ export const ProjectAccessAddUser = ({ roles }: IProjectAccessAddUserProps) => {
|
||||
}
|
||||
|
||||
try {
|
||||
await addUserToRole(id, role.id, user.id);
|
||||
await addUserToRole(projectId, role.id, user.id);
|
||||
refetchProjectAccess();
|
||||
setUser(undefined);
|
||||
setOptions([]);
|
||||
@ -129,9 +129,9 @@ export const ProjectAccessAddUser = ({ roles }: IProjectAccessAddUserProps) => {
|
||||
if (
|
||||
e
|
||||
.toString()
|
||||
.includes(`User already has access to project=${id}`)
|
||||
.includes(`User already has access to project=${projectId}`)
|
||||
) {
|
||||
error = `User already has access to project ${id}`;
|
||||
error = `User already has access to project ${projectId}`;
|
||||
} else {
|
||||
error = e.toString() || 'Server problems when adding users.';
|
||||
}
|
||||
|
||||
@ -8,17 +8,16 @@ import {
|
||||
SelectChangeEvent,
|
||||
} from '@mui/material';
|
||||
import { Delete } from '@mui/icons-material';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import {
|
||||
IProjectAccessOutput,
|
||||
IProjectAccessUser,
|
||||
} from 'hooks/api/getters/useProjectAccess/useProjectAccess';
|
||||
import { IProjectViewParams } from 'interfaces/params';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
||||
import { ProjectRoleSelect } from 'component/project/ProjectAccess/ProjectRoleSelect/ProjectRoleSelect';
|
||||
import { useStyles } from '../ProjectAccessListItem/ProjectAccessListItem.styles';
|
||||
import React from 'react';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IProjectAccessListItemProps {
|
||||
user: IProjectAccessUser;
|
||||
@ -33,7 +32,7 @@ export const ProjectAccessListItem = ({
|
||||
handleRoleChange,
|
||||
handleRemoveAccess,
|
||||
}: IProjectAccessListItemProps) => {
|
||||
const { id: projectId } = useParams<IProjectViewParams>();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
const labelId = `checkbox-list-secondary-label-${user.id}`;
|
||||
|
||||
@ -3,7 +3,7 @@ import { useStyles } from './ProjectCard.styles';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import { ReactComponent as ProjectIcon } from 'assets/icons/projectIcon.svg';
|
||||
import { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||
@ -36,7 +36,7 @@ export const ProjectCard = ({
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [showDelDialog, setShowDelDialog] = useState(false);
|
||||
const { deleteProject } = useProjectApi();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
|
||||
// @ts-expect-error
|
||||
@ -92,7 +92,7 @@ export const ProjectCard = ({
|
||||
<MenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
history.push(getProjectEditPath(id));
|
||||
navigate(getProjectEditPath(id));
|
||||
}}
|
||||
>
|
||||
<Edit className={classes.icon} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useContext, useMemo, useState } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { mutate } from 'swr';
|
||||
import { getProjectFetcher } from 'hooks/api/getters/useProject/getProjectFetcher';
|
||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||
@ -45,7 +45,7 @@ function resolveCreateButtonData(isOss: boolean, hasAccess: boolean) {
|
||||
|
||||
export const ProjectListNew = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
const { projects, loading, error, refetch } = useProjects();
|
||||
const [fetchedProjects, setFetchedProjects] = useState<projectMap>({});
|
||||
@ -94,12 +94,7 @@ export const ProjectListNew = () => {
|
||||
return (
|
||||
<Link
|
||||
key={project.id}
|
||||
to={{
|
||||
pathname: `/projects/${project.id}`,
|
||||
state: {
|
||||
projectName: project.name,
|
||||
},
|
||||
}}
|
||||
to={`/projects/${project.id}`}
|
||||
className={styles.cardLink}
|
||||
>
|
||||
<ProjectCard
|
||||
@ -151,7 +146,7 @@ export const ProjectListNew = () => {
|
||||
actions={
|
||||
<ResponsiveButton
|
||||
Icon={Add}
|
||||
onClick={() => history.push('/projects/create')}
|
||||
onClick={() => navigate('/projects/create')}
|
||||
maxWidth="700px"
|
||||
permission={CREATE_PROJECT}
|
||||
disabled={createButtonData.disabled}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { mutate, SWRConfig, useSWRConfig } from 'swr';
|
||||
import { useHistory } from 'react-router';
|
||||
import { useNavigate } from 'react-router';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import React from 'react';
|
||||
import { USER_ENDPOINT_PATH } from 'hooks/api/getters/useAuth/useAuthEndpoint';
|
||||
|
||||
interface ISWRProviderProps {
|
||||
isUnauthorized: () => boolean;
|
||||
isUnauthorized: boolean;
|
||||
}
|
||||
|
||||
const INVALID_TOKEN_ERROR = 'InvalidTokenError';
|
||||
@ -16,7 +16,7 @@ const SWRProvider: React.FC<ISWRProviderProps> = ({
|
||||
isUnauthorized,
|
||||
}) => {
|
||||
const { cache } = useSWRConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { setToastApiError } = useToast();
|
||||
|
||||
// @ts-expect-error
|
||||
@ -41,11 +41,11 @@ const SWRProvider: React.FC<ISWRProviderProps> = ({
|
||||
// @ts-expect-error
|
||||
cache.clear();
|
||||
|
||||
history.push('/login');
|
||||
navigate('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isUnauthorized()) {
|
||||
if (!isUnauthorized) {
|
||||
setToastApiError(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,7 +7,7 @@ import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import React, { useContext } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useSegmentForm } from '../hooks/useSegmentForm';
|
||||
import { SegmentForm } from '../SegmentForm/SegmentForm';
|
||||
@ -21,7 +21,7 @@ export const CreateSegment = () => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { showFeedbackCES } = useContext(feedbackCESContext);
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { createSegment, loading } = useSegmentsApi();
|
||||
const { refetchSegments } = useSegments();
|
||||
|
||||
@ -56,7 +56,7 @@ export const CreateSegment = () => {
|
||||
try {
|
||||
await createSegment(getSegmentPayload());
|
||||
await refetchSegments();
|
||||
history.push('/segments/');
|
||||
navigate('/segments/');
|
||||
setToastData({
|
||||
title: 'Segment created',
|
||||
confetti: true,
|
||||
|
||||
@ -8,7 +8,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import useToast from 'hooks/useToast';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useSegmentForm } from '../hooks/useSegmentForm';
|
||||
import { SegmentForm } from '../SegmentForm/SegmentForm';
|
||||
@ -24,7 +24,7 @@ export const EditSegment = () => {
|
||||
const { segment } = useSegment(Number(segmentId));
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { updateSegment, loading } = useSegmentsApi();
|
||||
const { refetchSegments } = useSegments();
|
||||
|
||||
@ -64,7 +64,7 @@ export const EditSegment = () => {
|
||||
try {
|
||||
await updateSegment(segment.id, getSegmentPayload());
|
||||
await refetchSegments();
|
||||
history.push('/segments/');
|
||||
navigate('/segments/');
|
||||
setToastData({
|
||||
title: 'Segment updated',
|
||||
type: 'success',
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Button } from '@mui/material';
|
||||
import Input from 'component/common/Input/Input';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useStyles } from 'component/segments/SegmentFormStepOne/SegmentFormStepOne.styles';
|
||||
import { SegmentFormStep } from '../SegmentForm/SegmentForm';
|
||||
import {
|
||||
@ -30,7 +30,7 @@ export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
|
||||
clearErrors,
|
||||
setCurrentStep,
|
||||
}) => {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
@ -78,7 +78,7 @@ export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
|
||||
type="button"
|
||||
className={styles.cancelButton}
|
||||
onClick={() => {
|
||||
history.push('/segments');
|
||||
navigate('/segments');
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
||||
import { IConstraint } from 'interfaces/strategy';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useStyles } from 'component/segments/SegmentFormStepTwo/SegmentFormStepTwo.styles';
|
||||
import {
|
||||
ConstraintAccordionList,
|
||||
@ -46,7 +46,7 @@ export const SegmentFormStepTwo: React.FC<ISegmentFormPartTwoProps> = ({
|
||||
mode,
|
||||
}) => {
|
||||
const constraintsAccordionListRef = useRef<IConstraintAccordionListRef>();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { classes: styles } = useStyles();
|
||||
const { context = [] } = useUnleashContext();
|
||||
@ -152,7 +152,7 @@ export const SegmentFormStepTwo: React.FC<ISegmentFormPartTwoProps> = ({
|
||||
type="button"
|
||||
className={styles.cancelButton}
|
||||
onClick={() => {
|
||||
history.push('/segments');
|
||||
navigate('/segments');
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
|
||||
@ -17,7 +17,7 @@ import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
||||
import { useSegmentsApi } from 'hooks/api/actions/useSegmentsApi/useSegmentsApi';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import PageContent from 'component/common/PageContent';
|
||||
@ -27,7 +27,7 @@ import { SegmentDocsWarning } from 'component/segments/SegmentDocs/SegmentDocs';
|
||||
import { NAVIGATE_TO_CREATE_SEGMENT } from 'utils/testIds';
|
||||
|
||||
export const SegmentsList = () => {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { segments = [], refetchSegments } = useSegments();
|
||||
const { deleteSegment } = useSegmentsApi();
|
||||
const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } =
|
||||
@ -95,7 +95,7 @@ export const SegmentsList = () => {
|
||||
title="Segments"
|
||||
actions={
|
||||
<PermissionButton
|
||||
onClick={() => history.push('/segments/create')}
|
||||
onClick={() => navigate('/segments/create')}
|
||||
permission={CREATE_SEGMENT}
|
||||
data-testid={NAVIGATE_TO_CREATE_SEGMENT}
|
||||
>
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import { ISegment } from 'interfaces/segment';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { SEGMENT_DELETE_BTN_ID } from 'utils/testIds';
|
||||
import React from 'react';
|
||||
|
||||
@ -34,7 +34,7 @@ export const SegmentListItem = ({
|
||||
setDelDialog,
|
||||
}: ISegmentListItemProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<TableRow className={styles.tableRow}>
|
||||
@ -63,7 +63,7 @@ export const SegmentListItem = ({
|
||||
<PermissionIconButton
|
||||
data-loading
|
||||
onClick={() => {
|
||||
push(`/segments/edit/${id}`);
|
||||
navigate(`/segments/edit/${id}`);
|
||||
}}
|
||||
permission={UPDATE_SEGMENT}
|
||||
tooltip="Edit segment"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Switch, Route, useHistory, Redirect } from 'react-router-dom';
|
||||
import { useNavigate, Navigate } from 'react-router-dom';
|
||||
import { SplashPageEnvironments } from '../SplashPageEnvironments/SplashPageEnvironments';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import useSplashApi from 'hooks/api/actions/useSplashApi/useSplashApi';
|
||||
@ -30,34 +30,29 @@ export const SplashPage = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route path="/splash/environments">
|
||||
<SplashPageEnvironments />
|
||||
</Route>
|
||||
<Route path="/splash/operators">
|
||||
<SplashPageOperators />
|
||||
</Route>
|
||||
<Route>
|
||||
<Redirect to="/" />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
switch (splashId) {
|
||||
case 'environments':
|
||||
return <SplashPageEnvironments />;
|
||||
case 'operators':
|
||||
return <SplashPageOperators />;
|
||||
default:
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
};
|
||||
|
||||
const useNavigationOnKeydown = (key: string, path: string) => {
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (event: KeyboardEvent) => {
|
||||
if (event.code === key) {
|
||||
push(path);
|
||||
navigate(path);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handler);
|
||||
return () => window.removeEventListener('keydown', handler);
|
||||
}, [key, path, push]);
|
||||
}, [key, path, navigate]);
|
||||
};
|
||||
|
||||
const isKnownSplashId = (value: string): value is SplashId => {
|
||||
|
||||
@ -4,14 +4,14 @@ import { VpnKey, CloudCircle } from '@mui/icons-material';
|
||||
import { useStyles } from 'component/splash/SplashPageEnvironments/SplashPageEnvironments.styles';
|
||||
import { ReactComponent as Logo1 } from 'assets/img/splashEnv1.svg';
|
||||
import { ReactComponent as Logo2 } from 'assets/img/splashEnv2.svg';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export const SplashPageEnvironments = () => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onFinish = () => {
|
||||
push('/');
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { useStyles } from 'component/splash/SplashPageOperators/SplashPageOperators.styles';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Button, IconButton } from '@mui/material';
|
||||
import { CloseOutlined } from '@mui/icons-material';
|
||||
import { OperatorUpgradeAlert } from 'component/common/OperatorUpgradeAlert/OperatorUpgradeAlert';
|
||||
|
||||
export const SplashPageOperators = () => {
|
||||
const { push } = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
@ -15,7 +15,7 @@ export const SplashPageOperators = () => {
|
||||
<h1 className={styles.title}>New strategy operators</h1>
|
||||
<IconButton
|
||||
className={styles.close}
|
||||
onClick={() => push('/')}
|
||||
onClick={() => navigate('/')}
|
||||
size="large"
|
||||
>
|
||||
<CloseOutlined titleAccess="Close" />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useAuthSplash } from 'hooks/api/getters/useAuth/useAuthSplash';
|
||||
import { useLocation, Redirect } from 'react-router-dom';
|
||||
import { useLocation, Navigate } from 'react-router-dom';
|
||||
import { matchPath } from 'react-router';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { IFlags } from 'interfaces/uiConfig';
|
||||
@ -18,7 +18,7 @@ export const SplashPageRedirect = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (matchPath(pathname, { path: '/splash/:splashId' })) {
|
||||
if (matchPath('/splash/:splashId', pathname)) {
|
||||
// We've already redirected to the splash page.
|
||||
return null;
|
||||
}
|
||||
@ -41,7 +41,7 @@ export const SplashPageRedirect = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Redirect to={`/splash/${showSplashId}`} />;
|
||||
return <Navigate to={`/splash/${showSplashId}`} replace />;
|
||||
};
|
||||
|
||||
const hasSeenSplashId = (splashId: SplashId, splash: IAuthSplash): boolean => {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
@ -13,7 +13,7 @@ import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||
export const CreateStrategy = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
strategyName,
|
||||
strategyDesc,
|
||||
@ -41,7 +41,7 @@ export const CreateStrategy = () => {
|
||||
try {
|
||||
await createStrategy(payload);
|
||||
refetchStrategies();
|
||||
history.push(`/strategies/${strategyName}`);
|
||||
navigate(`/strategies/${strategyName}`);
|
||||
setToastData({
|
||||
title: 'Strategy created',
|
||||
text: 'Successfully created strategy',
|
||||
@ -64,7 +64,7 @@ export const CreateStrategy = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
@ -10,12 +10,13 @@ import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import useStrategy from 'hooks/api/getters/useStrategy/useStrategy';
|
||||
import { UpdateButton } from 'component/common/UpdateButton/UpdateButton';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const EditStrategy = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const { name } = useParams<{ name: string }>();
|
||||
const navigate = useNavigate();
|
||||
const name = useRequiredPathParam('name');
|
||||
const { strategy } = useStrategy(name);
|
||||
const {
|
||||
strategyName,
|
||||
@ -44,7 +45,7 @@ export const EditStrategy = () => {
|
||||
const payload = getStrategyPayload();
|
||||
try {
|
||||
await updateStrategy(payload);
|
||||
history.push(`/strategies/${strategyName}`);
|
||||
navigate(`/strategies/${strategyName}`);
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Success',
|
||||
@ -67,7 +68,7 @@ export const EditStrategy = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useContext, useState } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import {
|
||||
IconButton,
|
||||
@ -45,7 +45,7 @@ interface IDialogueMetaData {
|
||||
}
|
||||
|
||||
export const StrategiesList = () => {
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const { classes: styles } = useStyles();
|
||||
const smallScreen = useMediaQuery('(max-width:700px)');
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
@ -70,7 +70,7 @@ export const StrategiesList = () => {
|
||||
show={
|
||||
<PermissionIconButton
|
||||
data-testid={ADD_NEW_STRATEGY_ID}
|
||||
onClick={() => history.push('/strategies/create')}
|
||||
onClick={() => navigate('/strategies/create')}
|
||||
permission={CREATE_STRATEGY}
|
||||
tooltip="New strategy"
|
||||
>
|
||||
@ -79,7 +79,7 @@ export const StrategiesList = () => {
|
||||
}
|
||||
elseShow={
|
||||
<PermissionButton
|
||||
onClick={() => history.push('/strategies/create')}
|
||||
onClick={() => navigate('/strategies/create')}
|
||||
color="primary"
|
||||
permission={CREATE_STRATEGY}
|
||||
data-testid={ADD_NEW_STRATEGY_ID}
|
||||
@ -204,7 +204,7 @@ export const StrategiesList = () => {
|
||||
show={
|
||||
<PermissionIconButton
|
||||
onClick={() =>
|
||||
history.push(`/strategies/${strategy?.name}/edit`)
|
||||
navigate(`/strategies/${strategy?.name}/edit`)
|
||||
}
|
||||
permission={UPDATE_STRATEGY}
|
||||
tooltip="Edit strategy"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Grid } from '@mui/material';
|
||||
import { useParams, useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { UPDATE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
||||
@ -10,13 +10,14 @@ import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { Edit } from '@mui/icons-material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const StrategyView = () => {
|
||||
const { name } = useParams<{ name: string }>();
|
||||
const name = useRequiredPathParam('name');
|
||||
const { strategies } = useStrategies();
|
||||
const { features = [] } = useFeatures();
|
||||
const { applications } = useApplications();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const toggles = features.filter(toggle => {
|
||||
return toggle?.strategies?.find(strategy => strategy.name === name);
|
||||
@ -25,7 +26,7 @@ export const StrategyView = () => {
|
||||
const strategy = strategies.find(strategy => strategy.name === name);
|
||||
|
||||
const handleEdit = () => {
|
||||
history.push(`/strategies/${name}/edit`);
|
||||
navigate(`/strategies/${name}/edit`);
|
||||
};
|
||||
|
||||
if (!strategy) return null;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useTagTypeForm from '../TagTypeForm/useTagTypeForm';
|
||||
import TagTypeForm from '../TagTypeForm/TagTypeForm';
|
||||
import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||
@ -12,7 +12,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
const CreateTagType = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
tagName,
|
||||
tagDesc,
|
||||
@ -33,7 +33,7 @@ const CreateTagType = () => {
|
||||
const payload = getTagPayload();
|
||||
try {
|
||||
await createTag(payload);
|
||||
history.push('/tag-types');
|
||||
navigate('/tag-types');
|
||||
setToastData({
|
||||
title: 'Tag type created',
|
||||
confetti: true,
|
||||
@ -55,7 +55,7 @@ const CreateTagType = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { UPDATE_TAG_TYPE } from 'component/providers/AccessProvider/permissions';
|
||||
import useTagTypeForm from '../TagTypeForm/useTagTypeForm';
|
||||
import TagForm from '../TagTypeForm/TagTypeForm';
|
||||
@ -9,11 +9,13 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const EditTagType = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const { name } = useParams<{ name: string }>();
|
||||
const navigate = useNavigate();
|
||||
const name = useRequiredPathParam('name');
|
||||
const { tagType } = useTagType(name);
|
||||
const {
|
||||
tagName,
|
||||
@ -32,7 +34,7 @@ const EditTagType = () => {
|
||||
const payload = getTagPayload();
|
||||
try {
|
||||
await updateTagType(tagName, payload);
|
||||
history.push('/tag-types');
|
||||
navigate('/tag-types');
|
||||
setToastData({
|
||||
title: 'Tag type updated',
|
||||
type: 'success',
|
||||
@ -52,7 +54,7 @@ const EditTagType = () => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useContext, useState } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
@ -34,7 +34,7 @@ export const TagTypeList = () => {
|
||||
open: boolean;
|
||||
name?: string;
|
||||
}>({ open: false });
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const smallScreen = useMediaQuery('(max-width:700px)');
|
||||
const { deleteTagType } = useTagTypesApi();
|
||||
const { tagTypes, refetch } = useTagTypes();
|
||||
@ -70,7 +70,7 @@ export const TagTypeList = () => {
|
||||
<Tooltip title="Add tag type">
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
history.push('/tag-types/create')
|
||||
navigate('/tag-types/create')
|
||||
}
|
||||
size="large"
|
||||
>
|
||||
@ -83,7 +83,7 @@ export const TagTypeList = () => {
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
history.push('/tag-types/create')
|
||||
navigate('/tag-types/create')
|
||||
}
|
||||
>
|
||||
New tag type
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user