mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
Fix/cleanup (#276)
* chore: update changelog * 4.0.0-alpha.10 * fix: add dnd provider as top level wrapper * fix: add forgotten password link to password auth * fix: remove unecceesary dnd component * fix: check for usedTokenError
This commit is contained in:
parent
e81918ee48
commit
1f46d3b21b
@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
The latest version of this document is always available in
|
The latest version of this document is always available in
|
||||||
[releases][releases-url].
|
[releases][releases-url].
|
||||||
|
# 4.0.0-alpha.10
|
||||||
|
- fix: password
|
||||||
# 4.0.0-alpha.9
|
# 4.0.0-alpha.9
|
||||||
- fix: optimizations
|
- fix: optimizations
|
||||||
- feat: user profile
|
- feat: user profile
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "unleash-frontend",
|
"name": "unleash-frontend",
|
||||||
"description": "unleash your features",
|
"description": "unleash your features",
|
||||||
"version": "4.0.0-alpha.9",
|
"version": "4.0.0-alpha.10",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"unleash",
|
"unleash",
|
||||||
"feature toggle",
|
"feature toggle",
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { DndProvider } from 'react-dnd';
|
|
||||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function useDNDProviderElement(props) {
|
|
||||||
|
|
||||||
if (!props.children) return null;
|
|
||||||
|
|
||||||
return <DndProvider backend={HTML5Backend}>{props.children}</DndProvider>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DragAndDrop(props) {
|
|
||||||
const DNDElement = useDNDProviderElement(props);
|
|
||||||
return <React.Fragment>{DNDElement}</React.Fragment>;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import cloneDeep from 'lodash.clonedeep';
|
import cloneDeep from 'lodash.clonedeep';
|
||||||
@ -6,7 +6,6 @@ import arrayMove from 'array-move';
|
|||||||
import { Button } from '@material-ui/core';
|
import { Button } from '@material-ui/core';
|
||||||
|
|
||||||
import { Alert } from '@material-ui/lab';
|
import { Alert } from '@material-ui/lab';
|
||||||
import DragAndDrop from '../../common/drag-and-drop';
|
|
||||||
import HeaderTitle from '../../common/HeaderTitle';
|
import HeaderTitle from '../../common/HeaderTitle';
|
||||||
import { updateIndexInArray } from '../../common/util';
|
import { updateIndexInArray } from '../../common/util';
|
||||||
import styles from './strategy.module.scss';
|
import styles from './strategy.module.scss';
|
||||||
@ -27,7 +26,9 @@ const StrategiesList = props => {
|
|||||||
const [delStrategy, setDelStrategy] = useState(null);
|
const [delStrategy, setDelStrategy] = useState(null);
|
||||||
const [showCreateStrategy, setShowCreateStrategy] = useState(false);
|
const [showCreateStrategy, setShowCreateStrategy] = useState(false);
|
||||||
const [showAlert, setShowAlert] = useState(true);
|
const [showAlert, setShowAlert] = useState(true);
|
||||||
const [editableStrategies, updateEditableStrategies] = useState(cloneDeep(props.configuredStrategies));
|
const [editableStrategies, updateEditableStrategies] = useState(
|
||||||
|
cloneDeep(props.configuredStrategies)
|
||||||
|
);
|
||||||
const [editStrategyIndex, setEditStrategyIndex] = useState();
|
const [editStrategyIndex, setEditStrategyIndex] = useState();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -39,7 +40,11 @@ const StrategiesList = props => {
|
|||||||
|
|
||||||
const updateStrategy = index => strategy => {
|
const updateStrategy = index => strategy => {
|
||||||
const newStrategy = { ...strategy };
|
const newStrategy = { ...strategy };
|
||||||
const newStrategies = updateIndexInArray(editableStrategies, index, newStrategy);
|
const newStrategies = updateIndexInArray(
|
||||||
|
editableStrategies,
|
||||||
|
index,
|
||||||
|
newStrategy
|
||||||
|
);
|
||||||
updateEditableStrategies(newStrategies);
|
updateEditableStrategies(newStrategies);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -87,9 +92,13 @@ const StrategiesList = props => {
|
|||||||
const strategy = editableStrategies[delStrategy];
|
const strategy = editableStrategies[delStrategy];
|
||||||
|
|
||||||
if (!strategy.new) {
|
if (!strategy.new) {
|
||||||
await props.saveStrategies(props.configuredStrategies.filter((_, i) => i !== delStrategy));
|
await props.saveStrategies(
|
||||||
|
props.configuredStrategies.filter((_, i) => i !== delStrategy)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
updateEditableStrategies(editableStrategies.filter((_, i) => i !== delStrategy));
|
updateEditableStrategies(
|
||||||
|
editableStrategies.filter((_, i) => i !== delStrategy)
|
||||||
|
);
|
||||||
|
|
||||||
setDelStrategy(undefined);
|
setDelStrategy(undefined);
|
||||||
setShowDelDialog(null);
|
setShowDelDialog(null);
|
||||||
@ -100,7 +109,12 @@ const StrategiesList = props => {
|
|||||||
setEditStrategyIndex(undefined);
|
setEditStrategyIndex(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { strategies, configuredStrategies, featureToggleName, editable } = props;
|
const {
|
||||||
|
strategies,
|
||||||
|
configuredStrategies,
|
||||||
|
featureToggleName,
|
||||||
|
editable,
|
||||||
|
} = props;
|
||||||
|
|
||||||
const resolveStrategyDefinition = strategyName => {
|
const resolveStrategyDefinition = strategyName => {
|
||||||
if (!strategies || strategies.length === 0) {
|
if (!strategies || strategies.length === 0) {
|
||||||
@ -139,11 +153,12 @@ const StrategiesList = props => {
|
|||||||
strategy={editingStrategy}
|
strategy={editingStrategy}
|
||||||
updateStrategy={updateStrategy(editStrategyIndex)}
|
updateStrategy={updateStrategy(editStrategyIndex)}
|
||||||
saveStrategy={saveStrategy(editStrategyIndex)}
|
saveStrategy={saveStrategy(editStrategyIndex)}
|
||||||
strategyDefinition={resolveStrategyDefinition(editingStrategy.name)}
|
strategyDefinition={resolveStrategyDefinition(
|
||||||
|
editingStrategy.name
|
||||||
|
)}
|
||||||
onCancel={clearAll}
|
onCancel={clearAll}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<DragAndDrop>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={editable}
|
condition={editable}
|
||||||
show={
|
show={
|
||||||
@ -167,18 +182,28 @@ const StrategiesList = props => {
|
|||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={showAlert}
|
condition={showAlert}
|
||||||
show={
|
show={
|
||||||
<Alert severity="info" className={styles.infoCard} onClose={() => setShowAlert(false)}>
|
<Alert
|
||||||
Strategies allow you fine grained control over how to activate your features, and are
|
severity="info"
|
||||||
composable blocks that are executed in an OR fashion. As an example, you can have a gradual
|
className={styles.infoCard}
|
||||||
rollout that targets 80% of users in a region of the world (using the enterprise feature of
|
onClose={() => setShowAlert(false)}
|
||||||
constraints), and another gradual rollout that targets 20% of the users in another region.
|
>
|
||||||
If you don't add a strategy, the default strategy is activated which means that the feature
|
Strategies allow you fine grained control over how to
|
||||||
will be strictly on/off for your entire userbase.
|
activate your features, and are composable blocks that
|
||||||
|
are executed in an OR fashion. As an example, you can
|
||||||
|
have a gradual rollout that targets 80% of users in a
|
||||||
|
region of the world (using the enterprise feature of
|
||||||
|
constraints), and another gradual rollout that targets
|
||||||
|
20% of the users in another region. If you don't add a
|
||||||
|
strategy, the default strategy is activated which means
|
||||||
|
that the feature will be strictly on/off for your entire
|
||||||
|
userbase.
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={!configuredStrategies || configuredStrategies.length === 0}
|
condition={
|
||||||
|
!configuredStrategies || configuredStrategies.length === 0
|
||||||
|
}
|
||||||
show={
|
show={
|
||||||
<p style={{ padding: '0 16px' }}>
|
<p style={{ padding: '0 16px' }}>
|
||||||
<i>No activation strategies selected.</i>
|
<i>No activation strategies selected.</i>
|
||||||
@ -199,7 +224,6 @@ const StrategiesList = props => {
|
|||||||
condition={cards.length > 0}
|
condition={cards.length > 0}
|
||||||
show={<div className={styles.strategyListCards}>{cards}</div>}
|
show={<div className={styles.strategyListCards}>{cards}</div>}
|
||||||
/>
|
/>
|
||||||
</DragAndDrop>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import { Button, TextField, Typography, IconButton } from '@material-ui/core';
|
||||||
Button,
|
|
||||||
TextField,
|
|
||||||
Typography,
|
|
||||||
IconButton,
|
|
||||||
} from '@material-ui/core';
|
|
||||||
import ConditionallyRender from '../../common/ConditionallyRender';
|
import ConditionallyRender from '../../common/ConditionallyRender';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { useCommonStyles } from '../../../common.styles';
|
import { useCommonStyles } from '../../../common.styles';
|
||||||
import { useStyles } from './PasswordAuth.styles';
|
import { useStyles } from './PasswordAuth.styles';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
||||||
const commonStyles = useCommonStyles();
|
const commonStyles = useCommonStyles();
|
||||||
@ -76,11 +72,7 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
|||||||
const { usernameError, passwordError, apiError } = errors;
|
const { usernameError, passwordError, apiError } = errors;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form onSubmit={handleSubmit} action={authDetails.path}>
|
||||||
onSubmit={handleSubmit}
|
|
||||||
action={authDetails.path}
|
|
||||||
>
|
|
||||||
|
|
||||||
<Typography variant="subtitle2" className={styles.apiError}>
|
<Typography variant="subtitle2" className={styles.apiError}>
|
||||||
{apiError}
|
{apiError}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -113,6 +105,11 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
|||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Link to="/forgotten-password">
|
||||||
|
<Typography variant="body2">
|
||||||
|
Forgot your password?
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
@ -129,10 +126,13 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
|||||||
const renderWithOptions = options => (
|
const renderWithOptions = options => (
|
||||||
<div>
|
<div>
|
||||||
{options.map(o => (
|
{options.map(o => (
|
||||||
<div key={o.type} className={classnames(
|
<div
|
||||||
|
key={o.type}
|
||||||
|
className={classnames(
|
||||||
styles.contentContainer,
|
styles.contentContainer,
|
||||||
commonStyles.contentSpacingY
|
commonStyles.contentSpacingY
|
||||||
)}>
|
)}
|
||||||
|
>
|
||||||
<Button color="primary" variant="contained" href={o.path}>
|
<Button color="primary" variant="contained" href={o.path}>
|
||||||
{o.value}
|
{o.value}
|
||||||
</Button>
|
</Button>
|
||||||
@ -142,7 +142,7 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
|||||||
condition={showFields}
|
condition={showFields}
|
||||||
show={renderLoginForm()}
|
show={renderLoginForm()}
|
||||||
elseShow={
|
elseShow={
|
||||||
<IconButton onClick={onShowOptions} >
|
<IconButton onClick={onShowOptions}>
|
||||||
{' '}
|
{' '}
|
||||||
Show more options
|
Show more options
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -155,15 +155,12 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="subtitle1">
|
<Typography variant="subtitle1">{authDetails.message}</Typography>
|
||||||
{authDetails.message}
|
|
||||||
</Typography>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={options.length > 0}
|
condition={options.length > 0}
|
||||||
show={renderWithOptions(options)}
|
show={renderWithOptions(options)}
|
||||||
elseShow={renderLoginForm()}
|
elseShow={renderLoginForm()}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ const getFetcher = (token: string) => () =>
|
|||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
|
|
||||||
const INVALID_TOKEN_ERROR = 'InvalidTokenError';
|
const INVALID_TOKEN_ERROR = 'InvalidTokenError';
|
||||||
|
const USED_TOKEN_ERROR = 'UsedTokenError';
|
||||||
|
|
||||||
const useResetPassword = () => {
|
const useResetPassword = () => {
|
||||||
const query = useQueryParams();
|
const query = useQueryParams();
|
||||||
@ -30,7 +31,9 @@ const useResetPassword = () => {
|
|||||||
setLoading(!error && !data);
|
setLoading(!error && !data);
|
||||||
}, [data, error]);
|
}, [data, error]);
|
||||||
|
|
||||||
const invalidToken = !loading && data?.name === INVALID_TOKEN_ERROR;
|
const invalidToken =
|
||||||
|
(!loading && data?.name === INVALID_TOKEN_ERROR) ||
|
||||||
|
data?.name === USED_TOKEN_ERROR;
|
||||||
|
|
||||||
return { token, data, error, loading, setLoading, invalidToken, retry };
|
return { token, data, error, loading, setLoading, invalidToken, retry };
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,8 @@ import { Provider } from 'react-redux';
|
|||||||
import { ThemeProvider, CssBaseline } from '@material-ui/core';
|
import { ThemeProvider, CssBaseline } from '@material-ui/core';
|
||||||
import thunkMiddleware from 'redux-thunk';
|
import thunkMiddleware from 'redux-thunk';
|
||||||
import { createStore, applyMiddleware, compose } from 'redux';
|
import { createStore, applyMiddleware, compose } from 'redux';
|
||||||
|
import { DndProvider } from 'react-dnd';
|
||||||
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||||
import { StylesProvider } from '@material-ui/core/styles';
|
import { StylesProvider } from '@material-ui/core/styles';
|
||||||
|
|
||||||
import mainTheme from './themes/main-theme';
|
import mainTheme from './themes/main-theme';
|
||||||
@ -39,6 +41,7 @@ metricsPoller.start();
|
|||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={unleashStore}>
|
<Provider store={unleashStore}>
|
||||||
|
<DndProvider backend={HTML5Backend}>
|
||||||
<AccessProvider store={unleashStore}>
|
<AccessProvider store={unleashStore}>
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
<ThemeProvider theme={mainTheme}>
|
<ThemeProvider theme={mainTheme}>
|
||||||
@ -51,6 +54,7 @@ ReactDOM.render(
|
|||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</AccessProvider>
|
</AccessProvider>
|
||||||
|
</DndProvider>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('app')
|
document.getElementById('app')
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user