diff --git a/frontend/web/components/feature-summary/FeatureRow.tsx b/frontend/web/components/feature-summary/FeatureRow.tsx index 37f7c094313c..91b8cf657d0f 100644 --- a/frontend/web/components/feature-summary/FeatureRow.tsx +++ b/frontend/web/components/feature-summary/FeatureRow.tsx @@ -2,7 +2,6 @@ import React, { FC, useCallback, useEffect, useMemo } from 'react' import ConfirmToggleFeature from 'components/modals/ConfirmToggleFeature' import ConfirmRemoveFeature from 'components/modals/ConfirmRemoveFeature' import CreateFlagModal from 'components/modals/create-feature' -import CreateExperimentModal from 'components/modals/create-experiment' import ProjectStore from 'common/stores/project-store' import Constants from 'common/constants' import { useProtectedTags } from 'common/utils/useProtectedTags' @@ -57,7 +56,6 @@ interface FeatureRowProps { releasePipelines?: ReleasePipeline[] onCloseEditModal?: () => void isCompact?: boolean - experimentMode?: boolean } const width = [220, 50, 55, 70, 450] @@ -69,7 +67,6 @@ const FeatureRow: FC = (props) => { disableControls, environmentFlags, environmentId, - experimentMode = false, fadeEnabled, fadeValue, hideAudit = false, @@ -114,9 +111,9 @@ const FeatureRow: FC = (props) => { const { feature } = Utils.fromParam() const { id } = projectFlag - const isModalOpen = - !!document?.getElementsByClassName('create-feature-modal')?.length || - !!document?.getElementsByClassName('create-experiment-modal')?.length + const isModalOpen = !!document?.getElementsByClassName( + 'create-feature-modal', + )?.length if (`${id}` === feature && !isModalOpen) { editFeature() } @@ -202,35 +199,18 @@ const FeatureRow: FC = (props) => { search: `?feature=${projectFlag.id}&tab=${tabValue}`, }) - const ModalComponent = experimentMode - ? CreateExperimentModal - : CreateFlagModal - const modalCssClass = experimentMode - ? 'side-modal create-feature-modal create-experiment-modal' - : 'side-modal create-feature-modal' - - const modalProps = experimentMode - ? { - environmentFlag, - environmentId, - history, - noPermissions: !permission, - projectFlag, - projectId, - tab, - } - : { - environmentFlag, - environmentId, - hasUnhealthyEvents: - isFeatureHealthEnabled && !!featureUnhealthyEvents?.length, - hideTagsByType: ['UNHEALTHY'], - history, - noPermissions: !permission, - projectFlag, - projectId, - tab, - } + const modalProps = { + environmentFlag, + environmentId, + hasUnhealthyEvents: + isFeatureHealthEnabled && !!featureUnhealthyEvents?.length, + hideTagsByType: ['UNHEALTHY'], + history, + noPermissions: !permission, + projectFlag, + projectId, + tab, + } const ownerChips = enforceFeatureOwners ? [ @@ -273,8 +253,8 @@ const FeatureRow: FC = (props) => { )} , - , - modalCssClass, + , + 'side-modal create-feature-modal', () => { if (onCloseEditModal) { return onCloseEditModal() diff --git a/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx b/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx deleted file mode 100644 index c668970014b4..000000000000 --- a/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import React, { FC, useMemo } from 'react' -import { - Bar, - BarChart, - CartesianGrid, - Cell, - LabelList, - Legend, - Pie, - PieChart, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, -} from 'recharts' -import { colorTextSecondary, colorTextSuccess } from 'common/theme/tokens' -import { useGetExperimentResultsQuery } from 'common/services/useExperimentResults' -import { buildChartColorMap } from 'components/charts' - -type ExperimentResultsTabProps = { - environmentId: string - featureName: string -} - -const ExperimentResultsTab: FC = ({ - environmentId, - featureName, -}) => { - const { data, error, isLoading } = useGetExperimentResultsQuery({ - environmentId, - featureName, - }) - - const variantColorMap = useMemo>(() => { - if (!data?.variants?.length) return {} - const winner = data.statistics?.winner - return { - ...buildChartColorMap( - data.variants.map((v: { variant: string }) => v.variant), - ), - ...(winner ? { [winner]: colorTextSuccess } : {}), - } - }, [data]) - - if (isLoading) { - return ( -
- -
- ) - } - - if (error || !data?.variants?.length) { - return ( -
- Experiment is on-going, data should start appearing as soon as your - users use it. -
- ) - } - const chanceToWinData = data.statistics?.chance_to_win - ? Object.entries(data.statistics.chance_to_win).map(([variant, value]) => ({ - chance: (value as number) * 100, - variant, - })) - : [] - - return ( -
- {/* Chart 1: Conversion Rate */} -
-
Conversion Rate (%)
- - - - - - - - `${v.toFixed(1)}%`} - /> - {data.variants.map((entry: { variant: string }) => ( - - ))} - - - -
- - {/* Chart 2: Evaluations & Conversions */} -
-
Evaluations & Conversions
- - - - - - - - - - - -
- - {/* Chart 3: Chance to Win */} - {chanceToWinData.length > 0 && ( -
-
Chance to Win (%)
- - - - `${variant}: ${chance.toFixed(1)}%` - } - > - {chanceToWinData.map((entry) => ( - - ))} - - - - - -
- )} - - {/* Statistics Summary */} - {data.statistics && ( -
-
Statistics
-
- {data.statistics.recommendation && ( -

- Recommendation:{' '} - {data.statistics.recommendation} -

- )} - {data.statistics.lift && ( -

- Lift: {data.statistics.lift} -

- )} - {data.statistics.p_value !== null && - data.statistics.p_value !== undefined && ( -

- P-value: {data.statistics.p_value} -

- )} - {data.statistics.sample_size_warning && ( -

- {data.statistics.sample_size_warning} -

- )} -
-
- )} -
- ) -} - -export default ExperimentResultsTab diff --git a/frontend/web/components/modals/create-experiment/index.js b/frontend/web/components/modals/create-experiment/index.js deleted file mode 100644 index ff6835a41417..000000000000 --- a/frontend/web/components/modals/create-experiment/index.js +++ /dev/null @@ -1,808 +0,0 @@ -import React, { Component } from 'react' -import cloneDeep from 'lodash/cloneDeep' -import find from 'lodash/find' -import ProjectStore from 'common/stores/project-store' -import ConfigProvider from 'common/providers/ConfigProvider' -import FeatureListStore from 'common/stores/feature-list-store' -import IdentityProvider from 'common/providers/IdentityProvider' -import Tabs from 'components/navigation/TabMenu/Tabs' -import TabItem from 'components/navigation/TabMenu/TabItem' -import ChangeRequestModal from 'components/modals/ChangeRequestModal' -import classNames from 'classnames' -import JSONReference from 'components/JSONReference' -import Permission from 'common/providers/Permission' -import { - setInterceptClose, - setModalTitle, -} from 'components/modals/base/ModalDefault' -import { getStore } from 'common/store' -import Button from 'components/base/forms/Button' -import { saveFeatureWithValidation } from 'components/saveFeatureWithValidation' -import { FlagValueFooter } from 'components/modals/FlagValueFooter' -import { getChangeRequests } from 'common/services/useChangeRequest' -import ProjectProvider from 'common/providers/ProjectProvider' -import CreateFeature from 'components/modals/create-feature/tabs/CreateFeatureTab' -import FeatureValueTab from 'components/modals/create-feature/tabs/FeatureValueTab' -import FeatureLimitAlert from 'components/modals/create-feature/components/FeatureLimitAlert' -import FeatureUpdateSummary from 'components/modals/create-feature/components/FeatureUpdateSummary' -import FeatureNameInput from 'components/modals/create-feature/components/FeatureNameInput' -import ExperimentResultsTab from './ExperimentResultsTab' -import moment from 'moment' - -const Index = class extends Component { - static displayName = 'create-experiment' - - constructor(props, context) { - super(props, context) - - const projectFlagData = this.props.projectFlag - ? cloneDeep(this.props.projectFlag) - : { - description: undefined, - is_archived: undefined, - is_server_key_only: undefined, - metadata: [], - multivariate_options: [], - name: undefined, - tags: [], - } - - const sourceFlag = this.props.identityFlag || this.props.environmentFlag - const environmentFlagData = sourceFlag ? cloneDeep(sourceFlag) : {} - - this.state = { - changeRequests: [], - environmentFlag: environmentFlagData, - featureLimitAlert: { percentage: 0 }, - isEdit: !!this.props.projectFlag, - projectFlag: projectFlagData, - scheduledChangeRequests: [], - valueChanged: false, - } - } - - close() { - closeModal() - } - - componentDidUpdate(prevProps) { - ES6Component(this) - - const environmentFlagSource = - this.props.identityFlag || this.props.environmentFlag - const prevEnvironmentFlagSource = - prevProps.identityFlag || prevProps.environmentFlag - - if ( - environmentFlagSource && - prevEnvironmentFlagSource && - environmentFlagSource.updated_at && - prevEnvironmentFlagSource.updated_at && - environmentFlagSource.updated_at !== prevEnvironmentFlagSource.updated_at - ) { - this.setState({ - environmentFlag: cloneDeep(environmentFlagSource), - }) - } - - if ( - this.props.projectFlag && - prevProps.projectFlag && - this.props.projectFlag.updated_at && - prevProps.projectFlag.updated_at && - this.props.projectFlag.updated_at !== prevProps.projectFlag.updated_at - ) { - this.setState({ - projectFlag: cloneDeep(this.props.projectFlag), - }) - } - - if ( - !this.props.identity && - this.props.environmentVariations !== prevProps.environmentVariations - ) { - if ( - this.props.environmentVariations && - this.props.environmentVariations.length - ) { - this.setState({ - projectFlag: { - ...this.state.projectFlag, - multivariate_options: - this.state.projectFlag.multivariate_options && - this.state.projectFlag.multivariate_options.map((v) => { - const matchingVariation = ( - this.props.multivariate_options || - this.props.environmentVariations - ).find((e) => e.multivariate_feature_option === v.id) - return { - ...v, - default_percentage_allocation: - (matchingVariation && - matchingVariation.percentage_allocation) || - v.default_percentage_allocation || - 0, - } - }), - }, - }) - } - } - } - - onClosing = () => { - if (this.state.isEdit) { - return new Promise((resolve) => { - if (this.state.valueChanged) { - openConfirm({ - body: 'Closing this will discard your unsaved changes.', - noText: 'Cancel', - onNo: () => resolve(false), - onYes: () => resolve(true), - title: 'Discard changes', - yesText: 'Ok', - }) - } else { - resolve(true) - } - }) - } - return Promise.resolve(true) - } - - componentDidMount = () => { - setInterceptClose(this.onClosing) - this.fetchChangeRequests() - this.fetchScheduledChangeRequests() - } - - componentWillUnmount() { - if (this.focusTimeout) { - clearTimeout(this.focusTimeout) - } - } - - save = (func, isSaving) => { - const { - environmentFlag, - environmentId, - identity, - identityFlag, - projectFlag: _projectFlag, - segmentOverrides, - } = this.props - const { environmentFlag: stateEnvironmentFlag, projectFlag } = this.state - const hasMultivariate = - environmentFlag && - environmentFlag.multivariate_feature_state_values && - environmentFlag.multivariate_feature_state_values.length - if (identity) { - !isSaving && - projectFlag.name && - func({ - environmentFlag, - environmentId, - identity, - identityFlag: Object.assign({}, identityFlag || {}, { - enabled: stateEnvironmentFlag.enabled, - feature_state_value: hasMultivariate - ? environmentFlag.feature_state_value - : this.cleanInputValue(stateEnvironmentFlag.feature_state_value), - multivariate_options: - stateEnvironmentFlag.multivariate_feature_state_values, - }), - projectFlag, - }) - } else { - FeatureListStore.isSaving = true - FeatureListStore.trigger('change') - !isSaving && - projectFlag.name && - func( - this.props.projectId, - this.props.environmentId, - { - default_enabled: stateEnvironmentFlag.enabled, - description: projectFlag.description, - initial_value: this.cleanInputValue( - stateEnvironmentFlag.feature_state_value, - ), - is_archived: projectFlag.is_archived, - is_server_key_only: projectFlag.is_server_key_only, - metadata: - !this.props.projectFlag?.metadata || - (this.props.projectFlag.metadata !== projectFlag.metadata && - projectFlag.metadata.length) - ? projectFlag.metadata - : this.props.projectFlag.metadata, - multivariate_options: projectFlag.multivariate_options, - name: projectFlag.name, - tags: projectFlag.tags, - }, - { - skipSaveProjectFeature: this.state.skipSaveProjectFeature, - ..._projectFlag, - }, - { - ...environmentFlag, - multivariate_feature_state_values: - this.props.environmentVariations || - environmentFlag?.multivariate_feature_state_values, - }, - segmentOverrides, - ) - } - } - - parseError = (error) => { - const { projectFlag } = this.props - let featureError = error?.message || error?.name?.[0] || error - let featureWarning = '' - if ( - featureError?.includes?.('no changes') && - projectFlag?.multivariate_options?.length - ) { - featureWarning = `Your feature contains no changes to its value, enabled state or environment weights. If you have adjusted any variation values this will have been saved for all environments.` - featureError = '' - } - return { featureError, featureWarning } - } - - cleanInputValue = (value) => { - if (value && typeof value === 'string') { - return value.trim() - } - return value - } - - fetchChangeRequests = (forceRefetch) => { - const { environmentId, projectFlag } = this.props - if (!projectFlag?.id) return - - getChangeRequests( - getStore(), - { - committed: false, - environmentId, - feature_id: projectFlag?.id, - }, - { forceRefetch }, - ).then((res) => { - this.setState({ changeRequests: res.data?.results }) - }) - } - - fetchScheduledChangeRequests = (forceRefetch) => { - const { environmentId, projectFlag } = this.props - if (!projectFlag?.id) return - - const date = moment().toISOString() - - getChangeRequests( - getStore(), - { - environmentId, - feature_id: projectFlag.id, - live_from_after: date, - }, - { forceRefetch }, - ).then((res) => { - this.setState({ scheduledChangeRequests: res.data?.results }) - }) - } - - render() { - const { environmentFlag, isEdit, projectFlag } = this.state - const { identity, identityName } = this.props - const Provider = identity ? IdentityProvider : FeatureListProvider - const environment = ProjectStore.getEnvironment(this.props.environmentId) - const isVersioned = !!environment?.use_v2_feature_versioning - const is4Eyes = - !!environment && - Utils.changeRequestsEnabled(environment.minimum_change_request_approvals) - const project = ProjectStore.model - const caseSensitive = project?.only_allow_lower_case_feature_names - const regex = project?.feature_name_regex - const controlValue = Utils.calculateControl( - projectFlag.multivariate_options, - ) - const invalid = - !!projectFlag.multivariate_options && - projectFlag.multivariate_options.length && - controlValue < 0 - const existingChangeRequest = this.props.changeRequest - const noPermissions = this.props.noPermissions - let regexValid = true - - try { - if (!isEdit && projectFlag.name && regex) { - regexValid = projectFlag.name.match(new RegExp(regex)) - } - } catch (e) { - regexValid = false - } - - return ( - - {({ project }) => ( - { - if (identity) { - this.close() - } - AppActions.refreshFeatures( - this.props.projectId, - this.props.environmentId, - ) - - if (is4Eyes && !identity) { - this.fetchChangeRequests(true) - this.fetchScheduledChangeRequests(true) - } - - if (this.props.changeRequest) { - this.close() - } - }} - > - {( - { error, isSaving }, - { createChangeRequest, createFlag, editFeatureValue }, - ) => { - const saveFeatureValue = saveFeatureWithValidation((schedule) => { - if ((is4Eyes || schedule) && !identity) { - this.setState({ valueChanged: false }) - const featureStates = [ - Object.assign({}, this.props.environmentFlag, { - enabled: environmentFlag.enabled, - feature_state_value: Utils.valueToFeatureState( - environmentFlag.feature_state_value, - ), - multivariate_feature_state_values: - environmentFlag.multivariate_feature_state_values, - }), - ] - - const getModalTitle = () => { - if (schedule) { - return 'New Scheduled Flag Update' - } - if (this.props.changeRequest) { - return 'Update Change Request' - } - return 'New Change Request' - } - - openModal2( - getModalTitle(), - { - closeModal2() - this.save( - ( - projectId, - environmentId, - flag, - projectFlag, - environmentFlag, - segmentOverrides, - ) => { - createChangeRequest( - projectId, - environmentId, - flag, - projectFlag, - environmentFlag, - segmentOverrides, - { - approvals, - description, - featureStateId: - this.props.changeRequest && - this.props.changeRequest.feature_states?.[0] - ?.id, - id: - this.props.changeRequest && - this.props.changeRequest.id, - ignore_conflicts, - live_from, - multivariate_options: flag.multivariate_options, - title, - }, - !is4Eyes, - ) - }, - ) - }} - />, - ) - } else { - this.setState({ valueChanged: false }) - this.save(editFeatureValue, isSaving) - } - }) - - const onCreateFeature = saveFeatureWithValidation(() => { - this.save(createFlag, isSaving) - }) - return ( - - {({ permission: createFeature }) => ( - - {({ permission: _projectAdmin }) => { - this.state.skipSaveProjectFeature = !createFeature - - return ( -
- {isEdit && !identity ? ( - <> - this.forceUpdate()} - urlParam='tab' - history={this.props.history} - overflowX - > - - { - this.setState({ - environmentFlag: { - ...this.state.environmentFlag, - ...changes, - }, - valueChanged: true, - }) - }} - onProjectFlagChange={(changes) => { - this.setState({ - projectFlag: { - ...this.state.projectFlag, - ...changes, - }, - }) - }} - onRemoveMultivariateOption={ - this.props.removeMultivariateOption - } - /> - - - - - - Results - - } - > - - - - - ) : ( -
- - this.setState({ featureLimitAlert }) - } - /> -
- - this.setState({ - projectFlag: { - ...this.state.projectFlag, - name, - }, - }) - } - caseSensitive={caseSensitive} - regex={regex} - regexValid={regexValid} - autoFocus - /> -
- { - this.setState({ - environmentFlag: { - ...this.state.environmentFlag, - ...changes, - }, - valueChanged: true, - }) - }} - onProjectFlagChange={(changes) => { - this.setState({ - projectFlag: { - ...this.state.projectFlag, - ...changes, - }, - }) - }} - onRemoveMultivariateOption={ - this.props.removeMultivariateOption - } - featureError={ - this.parseError(error).featureError - } - featureWarning={ - this.parseError(error).featureWarning - } - /> - -
- )} - {identity && ( -
- {identity ? ( -
-

- This will update the feature value for the - user {identityName} in - - {' '} - { - find(project.environments, { - api_key: this.props.environmentId, - }).name - } - . - - { - ' Any segment overrides for this feature will now be ignored.' - } -

-
- ) : ( - '' - )} - -
- {identity && ( -
- -
- )} -
-
- )} -
- ) - }} -
- )} -
- ) - }} -
- )} -
- ) - } -} - -Index.propTypes = {} - -//This will remount the modal when a feature is created -const FeatureProvider = (WrappedComponent) => { - class HOC extends Component { - constructor(props) { - super(props) - this.state = { - ...props, - } - ES6Component(this) - } - - componentDidMount() { - ES6Component(this) - this.listenTo( - FeatureListStore, - 'saved', - ({ - changeRequest, - createdFlag, - error, - isCreate, - updatedChangeRequest, - } = {}) => { - if (error?.data?.metadata) { - error.data.metadata?.forEach((m) => { - if (Object.keys(m).length > 0) { - toast(m.non_field_errors[0], 'danger') - } - }) - } else if (error?.data) { - toast('Error updating the Flag', 'danger') - return - } else { - const isEditingChangeRequest = - this.props.changeRequest && changeRequest - const operation = createdFlag || isCreate ? 'Created' : 'Updated' - const type = changeRequest ? 'Change Request' : 'Feature' - - const toastText = isEditingChangeRequest - ? `Updated ${type}` - : `${operation} ${type}` - const toastAction = changeRequest - ? { - buttonText: 'Open', - onClick: () => { - closeModal() - this.props.history.push( - `/project/${this.props.projectId}/environment/${this.props.environmentId}/change-requests/${updatedChangeRequest?.id}`, - ) - }, - } - : undefined - - toast(toastText, 'success', undefined, toastAction) - } - const envFlags = FeatureListStore.getEnvironmentFlags() - - if (createdFlag) { - const projectFlag = FeatureListStore.getProjectFlags()?.find?.( - (flag) => flag.name === createdFlag, - ) - window.history.replaceState( - {}, - `${document.location.pathname}?feature=${projectFlag.id}`, - ) - const newEnvironmentFlag = envFlags?.[projectFlag.id] || {} - setModalTitle(`Edit Feature ${projectFlag.name}`) - this.setState({ - environmentFlag: { - ...this.state.environmentFlag, - ...(newEnvironmentFlag || {}), - }, - projectFlag, - valueChanged: false, - }) - } else if (this.props.projectFlag) { - const newEnvironmentFlag = - envFlags?.[this.props.projectFlag.id] || {} - const newProjectFlag = FeatureListStore.getProjectFlags()?.find?.( - (flag) => flag.id === this.props.projectFlag.id, - ) - this.setState({ - environmentFlag: { - ...this.state.environmentFlag, - ...(newEnvironmentFlag || {}), - }, - projectFlag: newProjectFlag, - valueChanged: false, - }) - } - }, - ) - } - - render() { - return ( - - ) - } - } - return HOC -} - -const WrappedCreateExperiment = ConfigProvider(Index) - -export default FeatureProvider(WrappedCreateExperiment) diff --git a/frontend/web/components/modals/create-feature/index.tsx b/frontend/web/components/modals/create-feature/index.tsx index 787186cd90ef..d5346bd15c17 100644 --- a/frontend/web/components/modals/create-feature/index.tsx +++ b/frontend/web/components/modals/create-feature/index.tsx @@ -65,7 +65,6 @@ type CreateFeatureModalProps = { noPermissions?: boolean disableCreate?: boolean highlightSegmentId?: number - defaultExperiment?: boolean history?: History multivariate_options?: MultivariateFeatureStateValue[] } & Partial @@ -95,7 +94,6 @@ const mergeEnvironmentWeights = (options: any[], variations: any[]): any[] => const CreateFeatureModal: FC = (props) => { const { changeRequest: existingChangeRequest, - defaultExperiment, disableCreate, environmentId, environmentVariations, @@ -862,7 +860,6 @@ const CreateFeatureModal: FC = (props) => { featureState={props.environmentFlag || environmentFlag} projectFlag={projectFlag} identity={identity} - defaultExperiment={defaultExperiment} overrideFeatureState={ props.identityFlag ? environmentFlag : null } diff --git a/frontend/web/components/modals/create-feature/tabs/CreateFeatureTab.tsx b/frontend/web/components/modals/create-feature/tabs/CreateFeatureTab.tsx index 0361a7e89805..45daf16afede 100644 --- a/frontend/web/components/modals/create-feature/tabs/CreateFeatureTab.tsx +++ b/frontend/web/components/modals/create-feature/tabs/CreateFeatureTab.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useEffect, useState } from 'react' +import React, { FC } from 'react' import { FeatureState, ProjectFlag } from 'common/types/responses' import FeatureValueTab from './FeatureValueTab' import FeatureSettingsTab from './FeatureSettingsTab' @@ -6,12 +6,8 @@ import ErrorMessage from 'components/ErrorMessage' import WarningMessage from 'components/WarningMessage' import { useHasPermission } from 'common/providers/Permission' import { ProjectPermission } from 'common/types/permissions.types' -import Switch from 'components/Switch' -import Tooltip from 'components/Tooltip' -import Icon from 'components/icons/Icon' import InfoMessage from 'components/InfoMessage' import { useGetProjectQuery } from 'common/services/useProject' -import { useCreateTagMutation, useGetTagsQuery } from 'common/services/useTag' type CreateFeatureTabProps = { projectId: number @@ -20,7 +16,6 @@ type CreateFeatureTabProps = { overrideFeatureState?: FeatureState projectFlag: ProjectFlag | null identity?: string - defaultExperiment?: boolean ownerIds?: number[] groupOwnerIds?: number[] onOwnerIdsChange?: (ids: number[]) => void @@ -34,7 +29,6 @@ type CreateFeatureTabProps = { } const CreateFeatureTab: FC = ({ - defaultExperiment, error, featureError, featureState, @@ -69,76 +63,6 @@ const CreateFeatureTab: FC = ({ const noPermissions = !createFeature && !projectAdmin - const showExperimentToggle = - Utils.getFlagsmithHasFeature('experimental_flags') && !identity - - const { data: tags } = useGetTagsQuery( - { projectId }, - { skip: !showExperimentToggle }, - ) - const [createTag] = useCreateTagMutation() - - const [isExperimentFlag, setIsExperimentFlag] = useState(!!defaultExperiment) - - const hasVariants = (projectFlag?.multivariate_options?.length ?? 0) > 0 - - // Auto-untoggle if all variants are removed - useEffect(() => { - if (!hasVariants && isExperimentFlag) { - setIsExperimentFlag(false) - if (projectFlag && tags) { - const experimentTag = tags.find( - (t) => t.label.toLowerCase() === 'experiment', - ) - if (experimentTag) { - onProjectFlagChange({ - ...projectFlag, - tags: projectFlag.tags.filter((id) => id !== experimentTag.id), - }) - } - } - } - }, [hasVariants]) // eslint-disable-line react-hooks/exhaustive-deps - - const handleExperimentToggle = useCallback( - async (checked: boolean) => { - if (!projectFlag || !tags) return - - setIsExperimentFlag(checked) - - let experimentTag = tags.find( - (t) => t.label.toLowerCase() === 'experiment', - ) - - if (checked) { - if (!experimentTag) { - experimentTag = await createTag({ - projectId, - tag: { - color: '#6A52CF', - description: 'Experiment flag', - label: 'experiment', - }, - }).unwrap() - } - if (experimentTag && !projectFlag.tags.includes(experimentTag.id)) { - onProjectFlagChange({ - ...projectFlag, - tags: [...projectFlag.tags, experimentTag.id], - }) - } - } else { - if (experimentTag) { - onProjectFlagChange({ - ...projectFlag, - tags: projectFlag.tags.filter((id) => id !== experimentTag?.id), - }) - } - } - }, - [projectFlag, tags, createTag, projectId, onProjectFlagChange], - ) - return ( <> @@ -163,30 +87,6 @@ const CreateFeatureTab: FC = ({ onProjectFlagChange={onProjectFlagChange} onRemoveMultivariateOption={onRemoveMultivariateOption} /> - {showExperimentToggle && ( - - - -
- Experiment flag -
- - - } - > - {!hasVariants - ? 'Add at least one variant to start an experiment' - : 'Tag this feature as an experiment'} -
-
- )} = ({ - defaultExperiment, - forcedTagIds, - pageTitle, -}) => { +const FeaturesPage: FC = ({ forcedTagIds, pageTitle }) => { const history = useHistory() const routeContext = useRouteContext() const projectId = routeContext.projectId! @@ -207,7 +202,6 @@ const FeaturesPage: FC = ({ environmentId={environmentId} history={history} projectId={projectId} - defaultExperiment={defaultExperiment} />, 'side-modal create-feature-modal', ) @@ -287,7 +281,6 @@ const FeaturesPage: FC = ({ removeFlag={removeFlag} projectFlag={projectFlag} isCompact={isCompact} - experimentMode={defaultExperiment} /> )} @@ -300,7 +293,6 @@ const FeaturesPage: FC = ({ toggleFlag, removeFlag, isCompact, - defaultExperiment, ], )