1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-09 11:14:29 +02:00
unleash.unleash/frontend/src/component/feature/strategy/strategies-list-component.jsx
Fredrik Strand Oseberg e8de5bd816 Fix/strategy constraints (#283)
* feat: redesign strategy cards

* fix: add version check to constraints

* fix: use flags

* fix: update icon and add tooltips

* fix: remove console logs

Co-authored-by: Ivar Conradi Østhus <ivarconr@gmail.com>
2021-05-04 21:25:06 +02:00

244 lines
8.3 KiB
JavaScript

import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import cloneDeep from 'lodash.clonedeep';
import arrayMove from 'array-move';
import { Button } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import HeaderTitle from '../../common/HeaderTitle';
import { updateIndexInArray } from '../../common/util';
import styles from './strategy.module.scss';
import StrategyCard from './StrategyCard';
import EditStrategyModal from './EditStrategyModal/EditStrategyModal';
import ConditionallyRender from '../../common/ConditionallyRender';
import CreateStrategy from './AddStrategy/AddStrategy';
import Dialogue from '../../common/Dialogue/Dialogue';
const cleanStrategy = strategy => ({
name: strategy.name,
parameters: cloneDeep(strategy.parameters),
constraints: cloneDeep(strategy.constraints || []),
});
const StrategiesList = props => {
const [showDelDialog, setShowDelDialog] = useState(false);
const [delStrategy, setDelStrategy] = useState(null);
const [showCreateStrategy, setShowCreateStrategy] = useState(false);
const [showAlert, setShowAlert] = useState(true);
const [editableStrategies, updateEditableStrategies] = useState(
cloneDeep(props.configuredStrategies)
);
const [editStrategyIndex, setEditStrategyIndex] = useState();
useEffect(() => {
if (!editStrategyIndex) {
updateEditableStrategies(cloneDeep(props.configuredStrategies));
}
/* eslint-disable-next-line */
}, [props.configuredStrategies]);
const updateStrategy = index => strategy => {
const newStrategy = { ...strategy };
const newStrategies = updateIndexInArray(
editableStrategies,
index,
newStrategy
);
updateEditableStrategies(newStrategies);
};
const saveStrategy = index => async () => {
const strategies = [...props.configuredStrategies];
const strategy = editableStrategies[index];
const cleanedStrategy = cleanStrategy(strategy);
if (strategy.new) {
strategies.push(cleanedStrategy);
} else {
strategies[index] = cleanedStrategy;
}
// store in server
await props.saveStrategies(strategies);
// update local state
updateStrategy(index)(cleanedStrategy, false);
setEditStrategyIndex(undefined);
};
const addStrategy = strategy => {
const strategies = [...editableStrategies];
strategies.push({ ...strategy });
updateEditableStrategies(strategies);
setEditStrategyIndex(strategies.length - 1);
};
const moveStrategy = async (index, toIndex) => {
if (!editStrategyIndex) {
// console.log(`move strategy from ${index} to ${toIndex}`);
const strategies = arrayMove(editableStrategies, index, toIndex);
await props.saveStrategies(strategies);
updateEditableStrategies(strategies);
}
};
const triggerDelDialog = index => {
setShowDelDialog(true);
setDelStrategy(index);
};
const removeStrategy = async () => {
const strategy = editableStrategies[delStrategy];
if (!strategy.new) {
await props.saveStrategies(
props.configuredStrategies.filter((_, i) => i !== delStrategy)
);
}
updateEditableStrategies(
editableStrategies.filter((_, i) => i !== delStrategy)
);
setDelStrategy(undefined);
setShowDelDialog(null);
};
const clearAll = () => {
updateEditableStrategies(cloneDeep(props.configuredStrategies));
setEditStrategyIndex(undefined);
};
const {
strategies,
configuredStrategies,
featureToggleName,
editable,
} = props;
const resolveStrategyDefinition = strategyName => {
if (!strategies || strategies.length === 0) {
return { name: 'Loading' };
}
return strategies.find(s => s.name === strategyName);
};
const cards = editableStrategies.map((strategy, i) => (
<StrategyCard
key={i}
strategy={strategy}
strategyDefinition={resolveStrategyDefinition(strategy.name)}
removeStrategy={() => triggerDelDialog(i)}
moveStrategy={moveStrategy}
editStrategy={() => setEditStrategyIndex(i)}
index={i}
movable
/>
));
const editingStrategy = editableStrategies[editStrategyIndex];
return (
<div>
<CreateStrategy
strategies={strategies}
showCreateStrategy={showCreateStrategy}
setShowCreateStrategy={setShowCreateStrategy}
featureToggleName={featureToggleName}
addStrategy={addStrategy}
/>
{editingStrategy ? (
<EditStrategyModal
strategy={editingStrategy}
updateStrategy={updateStrategy(editStrategyIndex)}
saveStrategy={saveStrategy(editStrategyIndex)}
strategyDefinition={resolveStrategyDefinition(
editingStrategy.name
)}
onCancel={clearAll}
/>
) : null}
<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)}
>
<div style={{ maxWidth: '800px' }}>
Activation strategies defines how you enable the new
feature to your users. Changes to the activation
strategies does not require redeployment of the
code.
<br />
<br />
Multiple activation strategies are composable blocks
that is executed in an OR fashion.
<br />
E.g. A gradual rollout activation strategy allows
you to gradually enable to feature to a subset of
your users without redeploy to production.
</div>
</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>}
/>
</div>
);
};
StrategiesList.propTypes = {
strategies: PropTypes.array.isRequired,
configuredStrategies: PropTypes.array.isRequired,
featureToggleName: PropTypes.string.isRequired,
saveStrategies: PropTypes.func,
editable: PropTypes.bool,
};
export default StrategiesList;