mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-09 11:14:29 +02:00
* 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>
244 lines
8.3 KiB
JavaScript
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;
|