1
0
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:
Fredrik Strand Oseberg 2021-04-27 09:48:00 +02:00 committed by GitHub
parent e81918ee48
commit 1f46d3b21b
7 changed files with 134 additions and 120 deletions

View File

@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
The latest version of this document is always available in
[releases][releases-url].
# 4.0.0-alpha.10
- fix: password
# 4.0.0-alpha.9
- fix: optimizations
- feat: user profile

View File

@ -1,7 +1,7 @@
{
"name": "unleash-frontend",
"description": "unleash your features",
"version": "4.0.0-alpha.9",
"version": "4.0.0-alpha.10",
"keywords": [
"unleash",
"feature toggle",

View File

@ -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>;
}

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import cloneDeep from 'lodash.clonedeep';
@ -6,7 +6,6 @@ import arrayMove from 'array-move';
import { Button } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import DragAndDrop from '../../common/drag-and-drop';
import HeaderTitle from '../../common/HeaderTitle';
import { updateIndexInArray } from '../../common/util';
import styles from './strategy.module.scss';
@ -27,7 +26,9 @@ const StrategiesList = props => {
const [delStrategy, setDelStrategy] = useState(null);
const [showCreateStrategy, setShowCreateStrategy] = useState(false);
const [showAlert, setShowAlert] = useState(true);
const [editableStrategies, updateEditableStrategies] = useState(cloneDeep(props.configuredStrategies));
const [editableStrategies, updateEditableStrategies] = useState(
cloneDeep(props.configuredStrategies)
);
const [editStrategyIndex, setEditStrategyIndex] = useState();
useEffect(() => {
@ -39,7 +40,11 @@ const StrategiesList = props => {
const updateStrategy = index => strategy => {
const newStrategy = { ...strategy };
const newStrategies = updateIndexInArray(editableStrategies, index, newStrategy);
const newStrategies = updateIndexInArray(
editableStrategies,
index,
newStrategy
);
updateEditableStrategies(newStrategies);
};
@ -87,9 +92,13 @@ const StrategiesList = props => {
const strategy = editableStrategies[delStrategy];
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);
setShowDelDialog(null);
@ -100,7 +109,12 @@ const StrategiesList = props => {
setEditStrategyIndex(undefined);
};
const { strategies, configuredStrategies, featureToggleName, editable } = props;
const {
strategies,
configuredStrategies,
featureToggleName,
editable,
} = props;
const resolveStrategyDefinition = strategyName => {
if (!strategies || strategies.length === 0) {
@ -139,67 +153,77 @@ const StrategiesList = props => {
strategy={editingStrategy}
updateStrategy={updateStrategy(editStrategyIndex)}
saveStrategy={saveStrategy(editStrategyIndex)}
strategyDefinition={resolveStrategyDefinition(editingStrategy.name)}
strategyDefinition={resolveStrategyDefinition(
editingStrategy.name
)}
onCancel={clearAll}
/>
) : null}
<DragAndDrop>
<ConditionallyRender
condition={editable}
show={
<HeaderTitle
title="Activation strategies"
actions={
<>
<Button
variant="contained"
disabled={!featureToggleName}
color="primary"
onClick={() => setShowCreateStrategy(true)}
>
Add strategy
</Button>
</>
}
/>
}
/>
<ConditionallyRender
condition={showAlert}
show={
<Alert severity="info" className={styles.infoCard} onClose={() => setShowAlert(false)}>
Strategies allow you fine grained control over how to 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>
}
/>
<ConditionallyRender
condition={!configuredStrategies || configuredStrategies.length === 0}
show={
<p style={{ padding: '0 16px' }}>
<i>No activation strategies selected.</i>
</p>
}
/>
<ConditionallyRender
condition={editable}
show={
<HeaderTitle
title="Activation strategies"
actions={
<>
<Button
variant="contained"
disabled={!featureToggleName}
color="primary"
onClick={() => setShowCreateStrategy(true)}
>
Add strategy
</Button>
</>
}
/>
}
/>
<ConditionallyRender
condition={showAlert}
show={
<Alert
severity="info"
className={styles.infoCard}
onClose={() => setShowAlert(false)}
>
Strategies allow you fine grained control over how to
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>
}
/>
<ConditionallyRender
condition={
!configuredStrategies || configuredStrategies.length === 0
}
show={
<p style={{ padding: '0 16px' }}>
<i>No activation strategies selected.</i>
</p>
}
/>
<Dialogue
title="Really delete strategy?"
open={showDelDialog}
onClick={() => removeStrategy()}
onClose={() => {
setDelStrategy(null);
setShowDelDialog(false);
}}
/>
<ConditionallyRender
condition={cards.length > 0}
show={<div className={styles.strategyListCards}>{cards}</div>}
/>
</DragAndDrop>
<Dialogue
title="Really delete strategy?"
open={showDelDialog}
onClick={() => removeStrategy()}
onClose={() => {
setDelStrategy(null);
setShowDelDialog(false);
}}
/>
<ConditionallyRender
condition={cards.length > 0}
show={<div className={styles.strategyListCards}>{cards}</div>}
/>
</div>
);
};

View File

@ -1,16 +1,12 @@
import React, { useState } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import {
Button,
TextField,
Typography,
IconButton,
} from '@material-ui/core';
import { Button, TextField, Typography, IconButton } from '@material-ui/core';
import ConditionallyRender from '../../common/ConditionallyRender';
import { useHistory } from 'react-router';
import { useCommonStyles } from '../../../common.styles';
import { useStyles } from './PasswordAuth.styles';
import { Link } from 'react-router-dom';
const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
const commonStyles = useCommonStyles();
@ -76,11 +72,7 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
const { usernameError, passwordError, apiError } = errors;
return (
<form
onSubmit={handleSubmit}
action={authDetails.path}
>
<form onSubmit={handleSubmit} action={authDetails.path}>
<Typography variant="subtitle2" className={styles.apiError}>
{apiError}
</Typography>
@ -113,6 +105,11 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
size="small"
/>
<Link to="/forgotten-password">
<Typography variant="body2">
Forgot your password?
</Typography>
</Link>
<Button
variant="contained"
color="primary"
@ -129,11 +126,14 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
const renderWithOptions = options => (
<div>
{options.map(o => (
<div key={o.type} className={classnames(
styles.contentContainer,
commonStyles.contentSpacingY
)}>
<Button color="primary" variant="contained" href={o.path}>
<div
key={o.type}
className={classnames(
styles.contentContainer,
commonStyles.contentSpacingY
)}
>
<Button color="primary" variant="contained" href={o.path}>
{o.value}
</Button>
</div>
@ -142,7 +142,7 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
condition={showFields}
show={renderLoginForm()}
elseShow={
<IconButton onClick={onShowOptions} >
<IconButton onClick={onShowOptions}>
{' '}
Show more options
</IconButton>
@ -155,15 +155,12 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
return (
<div>
<Typography variant="subtitle1">
{authDetails.message}
</Typography>
<ConditionallyRender
condition={options.length > 0}
show={renderWithOptions(options)}
elseShow={renderLoginForm()}
/>
<Typography variant="subtitle1">{authDetails.message}</Typography>
<ConditionallyRender
condition={options.length > 0}
show={renderWithOptions(options)}
elseShow={renderLoginForm()}
/>
</div>
);
};

View File

@ -8,6 +8,7 @@ const getFetcher = (token: string) => () =>
}).then(res => res.json());
const INVALID_TOKEN_ERROR = 'InvalidTokenError';
const USED_TOKEN_ERROR = 'UsedTokenError';
const useResetPassword = () => {
const query = useQueryParams();
@ -30,7 +31,9 @@ const useResetPassword = () => {
setLoading(!error && !data);
}, [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 };
};

View File

@ -8,6 +8,8 @@ import { Provider } from 'react-redux';
import { ThemeProvider, CssBaseline } from '@material-ui/core';
import thunkMiddleware from 'redux-thunk';
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 mainTheme from './themes/main-theme';
@ -39,18 +41,20 @@ metricsPoller.start();
ReactDOM.render(
<Provider store={unleashStore}>
<AccessProvider store={unleashStore}>
<HashRouter>
<ThemeProvider theme={mainTheme}>
<StylesProvider injectFirst>
<CssBaseline />
<ScrollToTop>
<Route path="/" component={App} />
</ScrollToTop>
</StylesProvider>
</ThemeProvider>
</HashRouter>
</AccessProvider>
<DndProvider backend={HTML5Backend}>
<AccessProvider store={unleashStore}>
<HashRouter>
<ThemeProvider theme={mainTheme}>
<StylesProvider injectFirst>
<CssBaseline />
<ScrollToTop>
<Route path="/" component={App} />
</ScrollToTop>
</StylesProvider>
</ThemeProvider>
</HashRouter>
</AccessProvider>
</DndProvider>
</Provider>,
document.getElementById('app')
);