diff --git a/frontend/common/providers/FeatureListProvider.js b/frontend/common/providers/FeatureListProvider.js index 998082dcd616..8460bfdb4319 100644 --- a/frontend/common/providers/FeatureListProvider.js +++ b/frontend/common/providers/FeatureListProvider.js @@ -216,7 +216,16 @@ const FeatureListProvider = class extends React.Component { newProjectFlag, { ...environmentFlag, - multivariate_feature_state_values: flag.multivariate_options, + multivariate_feature_state_values: Utils.mapMvOptionsToStateValues( + newProjectFlag.multivariate_options?.map((opt, i) => ({ + ...opt, + default_percentage_allocation: + flag.multivariate_options?.[i] + ?.default_percentage_allocation ?? + opt.default_percentage_allocation, + })), + environmentFlag.multivariate_feature_state_values, + ), }, segmentOverrides, changeRequest, diff --git a/frontend/common/stores/feature-list-store.ts b/frontend/common/stores/feature-list-store.ts index 1b44eaa9fdc8..f43550c575bc 100644 --- a/frontend/common/stores/feature-list-store.ts +++ b/frontend/common/stores/feature-list-store.ts @@ -60,7 +60,18 @@ const convertSegmentOverrideToFeatureState = ( feature_state_value: override.value, id: override.id, live_from: changeRequest?.live_from, - multivariate_feature_state_values: override.multivariate_options, + multivariate_feature_state_values: changeRequest + ? override.multivariate_options?.map((v) => ({ + id: v.id, + multivariate_feature_option: v.multivariate_feature_option ?? v.id, + percentage_allocation: + v.percentage_allocation ?? v.default_percentage_allocation, + })) + : override.multivariate_options?.map((v) => ({ + multivariate_feature_option: v.multivariate_feature_option ?? v.id, + percentage_allocation: + v.percentage_allocation ?? v.default_percentage_allocation, + })), toRemove: override.toRemove, } as Partial } @@ -203,7 +214,6 @@ const controller = { store.model && store.model.features ? store.model.features.find((v) => v.id === flag.id) : flag - Promise.all( (flag.multivariate_options || []).map((v, i) => { const originalMV = v.id @@ -631,13 +641,16 @@ const controller = { (v.multivariate_feature_option || v.id) === (m.multivariate_feature_option || m.id), ) - return { - ...v, - percentage_allocation: matching - ? typeof matching.percentage_allocation === 'number' + let percentage_allocation = v.percentage_allocation + if (matching) { + percentage_allocation = + typeof matching.percentage_allocation === 'number' ? matching.percentage_allocation : matching.default_percentage_allocation - : v.percentage_allocation, + } + return { + ...v, + percentage_allocation, } }, ) @@ -733,7 +746,7 @@ const controller = { getStore().dispatch( projectFlagService.util.invalidateTags(['ProjectFlag']), ) - if(!store.model) { + if (!store.model) { return } // Fetch and update the latest environment feature state diff --git a/frontend/common/utils/utils.tsx b/frontend/common/utils/utils.tsx index 25434274a790..cefdcffdeb4b 100644 --- a/frontend/common/utils/utils.tsx +++ b/frontend/common/utils/utils.tsx @@ -79,11 +79,13 @@ const Utils = Object.assign({}, require('./base/_utils'), { const variation = variations && variations.find((env) => env.multivariate_feature_option === v.id) - total += variation - ? variation.percentage_allocation - : typeof v.default_percentage_allocation === 'number' - ? v.default_percentage_allocation - : (v as any).percentage_allocation + if (variation) { + total += variation.percentage_allocation + } else if (typeof v.default_percentage_allocation === 'number') { + total += v.default_percentage_allocation + } else { + total += (v as any).percentage_allocation + } return null }) return 100 - total @@ -157,15 +159,21 @@ const Utils = Object.assign({}, require('./base/_utils'), { displayLimitAlert(type: string, percentage: number | undefined) { const envOrProject = type === 'segment overrides' ? 'environment' : 'project' - return percentage >= 100 ? ( - - ) : percentage ? ( - - ) : null + if (percentage >= 100) { + return ( + + ) + } + if (percentage) { + return ( + + ) + } + return null }, escapeHtml(html: string) { const text = document.createTextNode(html) @@ -477,11 +485,13 @@ const Utils = Object.assign({}, require('./base/_utils'), { }, getPlansPermission: (feature: PaidFeature) => { const isOrgPermission = feature !== '2FA' - const plans = isOrgPermission - ? AccountStore.getActiveOrgPlan() - ? [AccountStore.getActiveOrgPlan()] - : null - : AccountStore.getPlans() + let plans + if (isOrgPermission) { + const activeOrgPlan = AccountStore.getActiveOrgPlan() + plans = activeOrgPlan ? [activeOrgPlan] : null + } else { + plans = AccountStore.getPlans() + } if (!plans || !plans.length) { return false @@ -669,6 +679,23 @@ const Utils = Object.assign({}, require('./base/_utils'), { head.appendChild(script) }) }, + mapMvOptionsToStateValues( + mvOptions: MultivariateOption[], + existingMvFeatureStateValues: MultivariateFeatureStateValue[], + ): MultivariateFeatureStateValue[] { + return mvOptions?.map((mvOption) => { + const existing = existingMvFeatureStateValues?.find( + (e) => e.multivariate_feature_option === mvOption.id, + ) + return { + id: existing?.id, + multivariate_feature_option: mvOption.id, + percentage_allocation: + mvOption.default_percentage_allocation ?? + existing?.percentage_allocation, + } + }) + }, numberWithCommas(x: number) { if (typeof x !== 'number') return '' diff --git a/frontend/web/components/diff/DiffFeature.tsx b/frontend/web/components/diff/DiffFeature.tsx index d9859ece510d..6a217f7cbe3e 100644 --- a/frontend/web/components/diff/DiffFeature.tsx +++ b/frontend/web/components/diff/DiffFeature.tsx @@ -211,7 +211,7 @@ const DiffFeature: FC = ({ +
Variations{' '} {variationDiffs.totalChanges ? ( @@ -265,6 +265,7 @@ const DiffFeature: FC = ({ diffs={segmentDiffs.diffs} projectId={projectId} environmentId={environmentId} + projectFlag={projectFlag} /> )} diff --git a/frontend/web/components/diff/DiffSegmentOverrides.tsx b/frontend/web/components/diff/DiffSegmentOverrides.tsx index d73143562e3a..91a503aa6383 100644 --- a/frontend/web/components/diff/DiffSegmentOverrides.tsx +++ b/frontend/web/components/diff/DiffSegmentOverrides.tsx @@ -9,17 +9,20 @@ import Icon from 'components/Icon' import Tooltip from 'components/Tooltip' import { Link, useHistory } from 'react-router-dom' import DiffVariations from './DiffVariations' +import { ProjectFlag } from 'common/types/responses' type DiffSegmentOverride = { diff: TDiffSegmentOverride projectId: string environmentId: string + projectFlag: ProjectFlag | undefined } const widths = [200, 80, 105] const DiffSegmentOverride: FC = ({ diff, environmentId, + projectFlag, projectId, }) => { return ( @@ -71,7 +74,10 @@ const DiffSegmentOverride: FC = ({
{!!diff.variationDiff?.diffs && ( - + )}
@@ -82,10 +88,12 @@ type DiffSegmentOverridesType = { diffs: TDiffSegmentOverride[] | undefined projectId: string environmentId: string + projectFlag: ProjectFlag | undefined } const DiffSegmentOverrides: FC = ({ diffs, environmentId, + projectFlag, projectId, }) => { const history = useHistory() @@ -139,6 +147,7 @@ const DiffSegmentOverrides: FC = ({ {created.map((diff, i) => ( = ({ {deleted.map((diff, i) => ( = ({ {modified.map((diff, i) => ( = ({ {unChanged.map((diff, i) => ( = ({ diffs, projectFlag }) => { + const sortedDiffs = useMemo(() => { + if (!diffs || !projectFlag?.multivariate_options) return diffs + return [...diffs].sort((a, b) => { + const indexA = projectFlag.multivariate_options.findIndex( + (mv) => mv.id === a.variationOption, + ) + const indexB = projectFlag.multivariate_options.findIndex( + (mv) => mv.id === b.variationOption, + ) + return indexA - indexB + }) + }, [diffs, projectFlag]) const tableHeader = (
Value
@@ -24,7 +36,7 @@ const DiffVariations: FC = ({ diffs, projectFlag }) => { return ( <> {tableHeader} - {diffs?.map((diff, i) => { + {sortedDiffs?.map((diff, i) => { const variation = projectFlag?.multivariate_options?.find( (v) => v.id === diff.variationOption, ) diff --git a/frontend/web/components/modals/CreateFlag.js b/frontend/web/components/modals/CreateFlag.js index 53d6030ee166..88eacc618667 100644 --- a/frontend/web/components/modals/CreateFlag.js +++ b/frontend/web/components/modals/CreateFlag.js @@ -428,7 +428,12 @@ const CreateFlag = class extends Component { tags: this.state.tags, }, projectFlag, - environmentFlag, + { + ...environmentFlag, + multivariate_feature_state_values: + this.props.environmentVariations || + environmentFlag?.multivariate_feature_state_values, + }, segmentOverrides, ) } @@ -1021,12 +1026,15 @@ const CreateFlag = class extends Component { }), ]) + let modalTitle = 'New Change Request' + if (schedule) { + modalTitle = 'New Scheduled Flag Update' + } else if (this.props.changeRequest) { + modalTitle = 'Update Change Request' + } + openModal2( - schedule - ? 'New Scheduled Flag Update' - : this.props.changeRequest - ? 'Update Change Request' - : 'New Change Request', + modalTitle, { + const getButtonText = + () => { + if (isSaving) { + return existingChangeRequest + ? 'Updating Change Request' + : 'Creating Change Request' + } + return existingChangeRequest + ? 'Update Change Request' + : 'Create Change Request' + } + + const getScheduleButtonText = + () => { + if (isSaving) { + return existingChangeRequest + ? 'Updating Change Request' + : 'Scheduling Update' + } + return existingChangeRequest + ? 'Update Change Request' + : 'Schedule Update' + } + if ( isVersioned && is4Eyes @@ -1560,13 +1592,7 @@ const CreateFlag = class extends Component { !savePermission } > - {isSaving - ? existingChangeRequest - ? 'Updating Change Request' - : 'Creating Change Request' - : existingChangeRequest - ? 'Update Change Request' - : 'Create Change Request'} + {getButtonText()} , ) } @@ -1599,13 +1625,7 @@ const CreateFlag = class extends Component { !savePermission } > - {isSaving - ? existingChangeRequest - ? 'Updating Change Request' - : 'Scheduling Update' - : existingChangeRequest - ? 'Update Change Request' - : 'Schedule Update'} + {getScheduleButtonText()} )}