diff --git a/frontend/common/constants.ts b/frontend/common/constants.ts index 90671de8605a..ca05197fbc26 100644 --- a/frontend/common/constants.ts +++ b/frontend/common/constants.ts @@ -363,7 +363,7 @@ const Constants = { }, }, exampleAuditWebhook: `{ - "created_date": "2020-02-23T17:30:57.006318Z", + "created_date": "2020-02-23T17:30:57.006318Z", "log": "New Flag / Remote Config created: my_feature", "author": { "id": 3, @@ -435,6 +435,15 @@ const Constants = { }, "event_type": "FLAG_UPDATED" }`, + featurePanelTabs: { + ANALYTICS: 'analytics', + HISTORY: 'history', + IDENTITY_OVERRIDES: 'identity-overrides', + LINKS: 'links', + SEGMENT_OVERRIDES: 'segment-overrides', + SETTINGS: 'settings', + VALUE: 'value', + }, forms: { maxLength: { 'FEATURE_ID': 150, diff --git a/frontend/common/stores/feature-list-store.ts b/frontend/common/stores/feature-list-store.ts index 1cb63da71b85..90ab661130da 100644 --- a/frontend/common/stores/feature-list-store.ts +++ b/frontend/common/stores/feature-list-store.ts @@ -980,7 +980,7 @@ const controller = { const store = Object.assign({}, BaseStore, { getEnvironmentFlags() { - return store.model && store.model.keyedEnvironmentFeatures + return store?.model?.keyedEnvironmentFeatures }, getFeatureUsage() { return store.model && store.model.usageData diff --git a/frontend/common/types/responses.ts b/frontend/common/types/responses.ts index 7fc47ae297cd..911784354b5a 100644 --- a/frontend/common/types/responses.ts +++ b/frontend/common/types/responses.ts @@ -456,18 +456,17 @@ export type ProjectFlag = { export type FeatureListProviderData = { projectFlags: ProjectFlag[] | null - environmentFlags: FeatureState[] | null + environmentFlags: Record | undefined error: boolean isLoading: boolean } export type FeatureListProviderActions = { toggleFlag: ( - index: number, - environments: Environment[], - comment: string | null, - environmentFlags: FeatureState[], - projectFlags: ProjectFlag[], + projectId: string, + environmentId: string, + projectFlag: ProjectFlag, + environmentFlags: FeatureState | undefined, ) => void removeFlag: (projectId: string, projectFlag: ProjectFlag) => void } diff --git a/frontend/common/utils/getProtectedTags.ts b/frontend/common/utils/getProtectedTags.ts deleted file mode 100644 index 807fb0c05852..000000000000 --- a/frontend/common/utils/getProtectedTags.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { getStore } from 'common/store' -import { ProjectFlag, Tag } from 'common/types/responses' -import { tagService } from 'common/services/useTag' - -export const getProtectedTags = ( - projectFlag: ProjectFlag, - projectId: string, -) => { - const store = getStore() - const tags: Tag[] = - tagService.endpoints.getTags.select({ projectId: `${projectId}` })( - store.getState(), - ).data || [] - return projectFlag.tags - ?.filter((id) => { - const tag = tags.find((tag) => tag.id === id) - return tag?.is_permanent - }) - .map((id) => { - return tags.find((tag) => tag.id === id) - }) -} diff --git a/frontend/common/utils/useProtectedTags.ts b/frontend/common/utils/useProtectedTags.ts new file mode 100644 index 000000000000..e49a48ae12e8 --- /dev/null +++ b/frontend/common/utils/useProtectedTags.ts @@ -0,0 +1,27 @@ +import { ProjectFlag, Tag } from 'common/types/responses' +import { useGetTagsQuery } from 'common/services/useTag' + +export const useProtectedTags = ( + projectFlag: ProjectFlag, + projectId: string, + skip?: boolean, +): Tag[] | undefined => { + const { data: tags } = useGetTagsQuery( + { projectId }, + { skip: skip || !projectId }, + ) + + if (!tags) { + return undefined + } + + const protectedFlags = projectFlag.tags.reduce((acc, id) => { + const tag = tags?.find((t) => t.id === id) + if (tag?.is_permanent) { + acc.push(tag) + } + return acc + }, []) + + return protectedFlags +} diff --git a/frontend/web/components/CompareEnvironments.js b/frontend/web/components/CompareEnvironments.js index cfcb9fabd7a6..b2a416f70b59 100644 --- a/frontend/web/components/CompareEnvironments.js +++ b/frontend/web/components/CompareEnvironments.js @@ -255,6 +255,7 @@ class CompareEnvironments extends Component { condensed isCompareEnv fadeEnabled={fadeEnabled} + history={this.context.router.history} fadeValue={fadeValue} environmentFlags={this.state.environmentLeftFlags} projectFlags={this.state.projectFlagsLeft} @@ -285,6 +286,7 @@ class CompareEnvironments extends Component { isCompareEnv fadeEnabled={fadeEnabled} fadeValue={fadeValue} + history={this.context.router.history} environmentFlags={this.state.environmentRightFlags} projectFlags={this.state.projectFlagsRight} permission={permission} diff --git a/frontend/web/components/CompareFeatures.js b/frontend/web/components/CompareFeatures.js index 5bf1c1082b06..49417c515f99 100644 --- a/frontend/web/components/CompareFeatures.js +++ b/frontend/web/components/CompareFeatures.js @@ -141,6 +141,7 @@ class CompareEnvironments extends Component { permission={permission} environmentId={data.api_key} projectId={this.props.projectId} + history={this.context.router.history} index={i} canDelete={permission} toggleFlag={toggleFlag} diff --git a/frontend/web/components/CondensedFeatureRow.tsx b/frontend/web/components/CondensedFeatureRow.tsx new file mode 100644 index 000000000000..920d4e6419ce --- /dev/null +++ b/frontend/web/components/CondensedFeatureRow.tsx @@ -0,0 +1,121 @@ +import classNames from 'classnames' +import Switch from './Switch' +import { + FeatureListProviderData, + FeatureState, + ProjectFlag, +} from 'common/types/responses' +import FeatureValue from './FeatureValue' +import SegmentOverridesIcon from './SegmentOverridesIcon' +import IdentityOverridesIcon from './IdentityOverridesIcon' +import Constants from 'common/constants' + +export interface CondensedFeatureRowProps { + disableControls?: boolean + readOnly: boolean + projectFlag: ProjectFlag + environmentFlags: FeatureListProviderData['environmentFlags'] + permission?: boolean + editFeature: ( + projectFlag: ProjectFlag, + environmentFlag?: FeatureState, + tab?: string, + ) => void + onChange: () => void + style?: React.CSSProperties + className?: string + isCompact?: boolean + fadeEnabled?: boolean + fadeValue?: boolean + index: number +} + +const CondensedFeatureRow: React.FC = ({ + className, + disableControls, + editFeature, + environmentFlags, + fadeEnabled, + fadeValue, + index, + isCompact, + onChange, + permission, + projectFlag, + readOnly, + style, +}) => { + const { id } = projectFlag + const showPlusIndicator = + projectFlag?.is_num_identity_overrides_complete === false + + return ( + { + if (disableControls) return + !readOnly && editFeature(projectFlag, environmentFlags?.[id]) + }} + style={{ ...style }} + className={classNames('flex-row', { 'fs-small': isCompact }, className)} + > +
+ + + +
+ + +
+ permission && + !readOnly && + editFeature(projectFlag, environmentFlags?.[id]) + } + className={`flex-fill ${fadeValue ? 'faded' : ''}`} + > + +
+ + { + e.stopPropagation() + editFeature( + projectFlag, + environmentFlags?.[id], + Constants.featurePanelTabs.SEGMENT_OVERRIDES, + ) + }} + count={projectFlag.num_segment_overrides} + /> + { + e.stopPropagation() + editFeature( + projectFlag, + environmentFlags?.[id], + Constants.featurePanelTabs.IDENTITY_OVERRIDES, + ) + }} + count={projectFlag.num_identity_overrides} + showPlusIndicator={showPlusIndicator} + /> +
+
+
+ ) +} + +export default CondensedFeatureRow diff --git a/frontend/web/components/ExampleFeatureRow.tsx b/frontend/web/components/ExampleFeatureRow.tsx deleted file mode 100644 index 184309823f98..000000000000 --- a/frontend/web/components/ExampleFeatureRow.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { FC } from 'react' -import { FeatureState, ProjectFlag } from 'common/types/responses' -import { useGetTagsQuery } from 'common/services/useTag' -import FeatureRow from './FeatureRow' -import { flag } from 'ionicons/icons' - -type ExampleFeatureRowType = {} - -const ExampleFeatureRow: FC = ({}) => { - const flag: ProjectFlag = { - created_date: new Date().toISOString(), - default_enabled: true, - description: 'Feature description', - id: 1, - initial_value: 'Blue', - is_archived: false, - is_server_key_only: false, - multivariate_options: [], - name: 'example_feature', - num_identity_overrides: 1, - num_segment_overrides: 1, - owner_groups: [], - owners: [], - project: 1, - tags: [], - type: 'STANDARD', - uuid: '1', - } - const flag2: ProjectFlag = { - created_date: new Date().toISOString(), - default_enabled: true, - description: 'Feature description', - id: 1, - initial_value: `2`, - is_archived: false, - is_server_key_only: false, - multivariate_options: [], - name: 'example_feature2', - num_identity_overrides: 1, - num_segment_overrides: 1, - owner_groups: [], - owners: [], - project: 1, - tags: [], - type: 'STANDARD', - uuid: '1', - } - const featureState: FeatureState = { - created_at: '', - enabled: false, - environment: 1, - feature: flag.id, - feature_state_value: flag.initial_value, - id: 1, - multivariate_feature_state_values: [], - updated_at: '', - uuid: '', - } - return ( -
-
-
- {}} - removeFlag={() => {}} - projectFlag={flag} - /> - {}} - removeFlag={() => {}} - projectFlag={flag} - /> -
-
-
- ) -} - -export default ExampleFeatureRow diff --git a/frontend/web/components/FeatureRow.js b/frontend/web/components/FeatureRow.js deleted file mode 100644 index 53fb6f4da3e7..000000000000 --- a/frontend/web/components/FeatureRow.js +++ /dev/null @@ -1,447 +0,0 @@ -import React, { Component } from 'react' -import TagValues from './tags/TagValues' -import ConfirmToggleFeature from './modals/ConfirmToggleFeature' -import ConfirmRemoveFeature from './modals/ConfirmRemoveFeature' -import CreateFlagModal from './modals/CreateFlag' -import ProjectStore from 'common/stores/project-store' -import Constants from 'common/constants' -import { getProtectedTags } from 'common/utils/getProtectedTags' -import Icon from './Icon' -import FeatureValue from './FeatureValue' -import FeatureAction from './FeatureAction' -import { getViewMode } from 'common/useViewMode' -import classNames from 'classnames' -import Tag from './tags/Tag' -import Button from './base/forms/Button' -import SegmentOverridesIcon from './SegmentOverridesIcon' -import IdentityOverridesIcon from './IdentityOverridesIcon' -import StaleFlagWarning from './StaleFlagWarning' -import UnhealthyFlagWarning from './UnhealthyFlagWarning' - -export const width = [200, 70, 55, 70, 450] - -export const TABS = { - ANALYTICS: 'analytics', - HISTORY: 'history', - IDENTITY_OVERRIDES: 'identity-overrides', - LINKS: 'links', - SEGMENT_OVERRIDES: 'segment-overrides', - SETTINGS: 'settings', - VALUE: 'value', -} - -class TheComponent extends Component { - static contextTypes = { - router: propTypes.object.isRequired, - } - - constructor(props, context) { - super(props, context) - - this.state = { - unhealthyTagId: undefined, - } - } - - confirmToggle = () => { - const { - environmentFlags, - environmentId, - projectFlag, - projectId, - toggleFlag, - } = this.props - const { id } = projectFlag - openModal( - 'Toggle Feature', - { - toggleFlag( - projectId, - environmentId, - projectFlag, - environmentFlags[id], - ) - }} - />, - 'p-0', - ) - } - - componentDidMount() { - const { environmentFlags, projectFlag } = this.props - const { feature } = Utils.fromParam() - const { id } = projectFlag - if (`${id}` === feature) { - this.editFeature(projectFlag, environmentFlags[id]) - } - } - - copyFeature = (e) => { - const { projectFlag } = this.props - e?.stopPropagation()?.() - e?.currentTarget?.blur?.() - Utils.copyToClipboard(projectFlag.name) - } - confirmRemove = (projectFlag, cb) => { - openModal2( - 'Remove Feature', - , - 'p-0', - ) - } - - editFeature = (projectFlag, environmentFlag, tab) => { - if (this.props.disableControls) { - return - } - API.trackEvent(Constants.events.VIEW_FEATURE) - history.replaceState( - {}, - null, - `${document.location.pathname}?feature=${projectFlag.id}&tab=${ - tab || Utils.fromParam().tab || 'value' - }`, - ) - openModal( - - {this.props.permission ? 'Edit Feature' : 'Feature'}: {projectFlag.name} - - , - , - 'side-modal create-feature-modal', - () => { - history.replaceState({}, null, `${document.location.pathname}`) - }, - ) - } - - render() { - const { - disableControls, - environmentFlags, - environmentId, - permission, - projectFlag, - projectId, - removeFlag, - } = this.props - const { created_date, description, id, name } = this.props.projectFlag - const readOnly = - this.props.readOnly || Utils.getFlagsmithHasFeature('read_only_mode') - const protectedTags = getProtectedTags(projectFlag, projectId) - const environment = ProjectStore.getEnvironment(environmentId) - const changeRequestsEnabled = Utils.changeRequestsEnabled( - environment && environment.minimum_change_request_approvals, - ) - const showPlusIndicator = - projectFlag?.is_num_identity_overrides_complete === false - - const onChange = () => { - if (disableControls) { - return - } - if ( - projectFlag?.multivariate_options?.length || - Utils.changeRequestsEnabled( - environment.minimum_change_request_approvals, - ) - ) { - this.editFeature(projectFlag, environmentFlags[id]) - return - } - this.confirmToggle() - } - const isCompact = getViewMode() === 'compact' - if (this.props.condensed) { - return ( - { - if (disableControls) return - !readOnly && this.editFeature(projectFlag, environmentFlags[id]) - }} - style={{ - ...(this.props.style || {}), - }} - className={classNames( - 'flex-row', - { 'fs-small': isCompact }, - this.props.className, - )} - > -
- - - -
- - -
- permission && - !readOnly && - this.editFeature(projectFlag, environmentFlags[id]) - } - className={`flex-fill ${this.props.fadeValue ? 'faded' : ''}`} - > - -
- - { - e.stopPropagation() - this.editFeature( - projectFlag, - environmentFlags[id], - TABS.SEGMENT_OVERRIDES, - ) - }} - count={projectFlag.num_segment_overrides} - /> - { - e.stopPropagation() - this.editFeature( - projectFlag, - environmentFlags[id], - TABS.IDENTITY_OVERRIDES, - ) - }} - count={projectFlag.num_identity_overrides} - showPlusIndicator={showPlusIndicator} - /> -
-
-
- ) - } - - const isFeatureHealthEnabled = - Utils.getFlagsmithHasFeature('feature_health') - - return ( - - !readOnly && this.editFeature(projectFlag, environmentFlags[id]) - } - > - - - - - - - {created_date ? ( - {name}}> - {isCompact && description ? `${description}` : null} - - ) : ( - name - )} - - - - { - e.stopPropagation() - this.editFeature( - projectFlag, - environmentFlags[id], - TABS.SEGMENT_OVERRIDES, - ) - }} - count={projectFlag.num_segment_overrides} - /> - { - e.stopPropagation() - this.editFeature( - projectFlag, - environmentFlags[id], - TABS.IDENTITY_OVERRIDES, - ) - }} - count={projectFlag.num_identity_overrides} - showPlusIndicator={showPlusIndicator} - /> - {projectFlag.is_server_key_only && ( - - {'Server-side only'} - - } - place='top' - > - { - 'Prevent this feature from being accessed with client-side SDKs.' - } - - )} - - {projectFlag.is_archived && ( - - )} - - {!!isCompact && } - {isFeatureHealthEnabled && !!isCompact && ( - - )} - - {!isCompact && } - {isFeatureHealthEnabled && !isCompact && ( - - )} - {description && !isCompact && ( -
- {description} -
- )} -
-
-
-
- - !readOnly && this.editFeature(projectFlag, environmentFlags[id]) - } - value={ - environmentFlags[id] && environmentFlags[id].feature_state_value - } - data-test={`feature-value-${this.props.index}`} - /> -
-
{ - e.stopPropagation() - }} - > - -
- -
{ - e.stopPropagation() - }} - > - { - if (disableControls) return - this.editFeature(projectFlag, environmentFlags[id], TABS.HISTORY) - }} - onShowAudit={() => { - if (disableControls) return - this.context.router.history.push( - `/project/${projectId}/audit-log?env=${environment.id}&search=${projectFlag.name}`, - ) - }} - onRemove={() => { - if (disableControls) return - this.confirmRemove(projectFlag, () => { - removeFlag(projectId, projectFlag) - }) - }} - onCopyName={this.copyFeature} - /> -
-
- ) - } -} - -export default TheComponent diff --git a/frontend/web/components/FeatureRow.tsx b/frontend/web/components/FeatureRow.tsx new file mode 100644 index 000000000000..ccffd86c2693 --- /dev/null +++ b/frontend/web/components/FeatureRow.tsx @@ -0,0 +1,398 @@ +import React, { FC, useEffect } from 'react' +import TagValues from './tags/TagValues' +import ConfirmToggleFeature from './modals/ConfirmToggleFeature' +import ConfirmRemoveFeature from './modals/ConfirmRemoveFeature' +import CreateFlagModal from './modals/CreateFlag' +import ProjectStore from 'common/stores/project-store' +import Constants from 'common/constants' +import { useProtectedTags } from 'common/utils/useProtectedTags' +import Icon from './Icon' +import FeatureValue from './FeatureValue' +import FeatureAction from './FeatureAction' +import { getViewMode } from 'common/useViewMode' +import classNames from 'classnames' +import Tag from './tags/Tag' +import Button from './base/forms/Button' +import SegmentOverridesIcon from './SegmentOverridesIcon' +import IdentityOverridesIcon from './IdentityOverridesIcon' +import StaleFlagWarning from './StaleFlagWarning' +import UnhealthyFlagWarning from './UnhealthyFlagWarning' +import { + Environment, + FeatureListProviderActions, + FeatureListProviderData, + FeatureState, + ProjectFlag, +} from 'common/types/responses' +import Utils from 'common/utils/utils' +import API from 'project/api' +import Switch from './Switch' +import AccountStore from 'common/stores/account-store' +import CondensedFeatureRow from './CondensedFeatureRow' +import { RouterChildContext } from 'react-router' + +interface FeatureRowProps { + disableControls?: boolean + environmentFlags: FeatureListProviderData['environmentFlags'] + environmentId: string + permission?: boolean + projectFlag: ProjectFlag + projectId: string + removeFlag?: FeatureListProviderActions['removeFlag'] + toggleFlag?: FeatureListProviderActions['toggleFlag'] + index: number + readOnly?: boolean + condensed?: boolean + className?: string + style?: React.CSSProperties + fadeEnabled?: boolean + fadeValue?: boolean + hideAudit?: boolean + hideRemove?: boolean + history?: RouterChildContext['router']['history'] +} + +const width = [200, 70, 55, 70, 450] + +const FeatureRow: FC = ({ + className, + condensed = false, + disableControls, + environmentFlags, + environmentId, + fadeEnabled, + fadeValue, + hideAudit = false, + hideRemove = false, + history, + index, + permission, + projectFlag, + projectId, + readOnly = false, + removeFlag, + style, + toggleFlag, +}) => { + const protectedTags = useProtectedTags(projectFlag, projectId) + + useEffect(() => { + const { feature } = Utils.fromParam() + const { id } = projectFlag + + if (`${id}` === feature) { + editFeature(projectFlag, environmentFlags?.[id]) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [environmentFlags, projectFlag]) + + const copyFeature = () => { + Utils.copyToClipboard(projectFlag.name) + } + + const confirmRemove = (projectFlag: ProjectFlag, cb: () => void) => { + openModal2( + 'Remove Feature', + , + 'p-0', + ) + } + + const confirmToggle = () => { + const { id } = projectFlag + openModal( + 'Toggle Feature', + { + toggleFlag?.( + projectId, + environmentId, + projectFlag, + environmentFlags?.[id], + ) + }} + />, + 'p-0', + ) + } + + const onChange = () => { + if (disableControls) { + return + } + if ( + projectFlag?.multivariate_options?.length || + Utils.changeRequestsEnabled(environment?.minimum_change_request_approvals) + ) { + editFeature(projectFlag, environmentFlags?.[id]) + return + } + confirmToggle() + } + + const editFeature = ( + projectFlag: ProjectFlag, + environmentFlag?: FeatureState, + tab?: string, + ) => { + if (disableControls) { + return + } + + API.trackEvent(Constants.events.VIEW_FEATURE) + const tabValue = tab || Utils.fromParam().tab || 'value' + + history.replace( + {}, + '', + `${document.location.pathname}?feature=${projectFlag.id}&tab=${tabValue}`, + ) + + openModal( + + {permission ? 'Edit Feature' : 'Feature'}: {projectFlag.name} + + , + , + 'side-modal create-feature-modal', + () => { + history.replace({}, '', `${document.location.pathname}`) + }, + ) + } + + const isReadOnly = readOnly || Utils.getFlagsmithHasFeature('read_only_mode') + const isFeatureHealthEnabled = Utils.getFlagsmithHasFeature('feature_health') + + const { created_date, description, id, name } = projectFlag + const environment = ProjectStore.getEnvironment( + environmentId, + ) as Environment | null + + const isCompact = getViewMode() === 'compact' + const showPlusIndicator = + projectFlag?.is_num_identity_overrides_complete === false + + if (condensed) { + return ( + + ) + } + + return ( + + !isReadOnly && editFeature(projectFlag, environmentFlags?.[id]) + } + > + + + + + + + {created_date ? ( + + {isCompact && description ? `${description}` : ''} + + ) : ( + name + )} + + + + { + e.stopPropagation() + editFeature( + projectFlag, + environmentFlags?.[id], + Constants.featurePanelTabs.SEGMENT_OVERRIDES, + ) + }} + count={projectFlag.num_segment_overrides} + /> + { + e.stopPropagation() + editFeature( + projectFlag, + environmentFlags?.[id], + Constants.featurePanelTabs.IDENTITY_OVERRIDES, + ) + }} + count={projectFlag.num_identity_overrides} + showPlusIndicator={showPlusIndicator} + /> + {projectFlag.is_server_key_only && ( + + {'Server-side only'} + + } + place='top' + > + { + 'Prevent this feature from being accessed with client-side SDKs.' + } + + )} + + {projectFlag.is_archived && ( + + )} + + {!!isCompact && } + {isFeatureHealthEnabled && !!isCompact && ( + + )} + + {!isCompact && } + {isFeatureHealthEnabled && !isCompact && ( + + )} + {description && !isCompact && ( +
+ {description} +
+ )} +
+
+
+
+ + !isReadOnly && editFeature(projectFlag, environmentFlags?.[id]) + } + value={environmentFlags?.[id]?.feature_state_value ?? null} + data-test={`feature-value-${index}`} + /> +
+
{ + e.stopPropagation() + }} + > + +
+ +
{ + e.stopPropagation() + }} + > + { + if (disableControls) return + editFeature( + projectFlag, + environmentFlags?.[id], + Constants.featurePanelTabs.HISTORY, + ) + }} + onShowAudit={() => { + if (disableControls) return + history.push( + `/project/${projectId}/audit-log?env=${environment?.id}&search=${projectFlag.name}`, + '', + ) + }} + onRemove={() => { + if (disableControls) return + confirmRemove(projectFlag, () => { + removeFlag?.(projectId, projectFlag) + }) + }} + onCopyName={copyFeature} + /> +
+
+ ) +} + +export default FeatureRow diff --git a/frontend/web/components/StaleFlagWarning.tsx b/frontend/web/components/StaleFlagWarning.tsx index 2cb576f7a301..3acf786a43d1 100644 --- a/frontend/web/components/StaleFlagWarning.tsx +++ b/frontend/web/components/StaleFlagWarning.tsx @@ -2,7 +2,7 @@ import { FC } from 'react' import Constants from 'common/constants' import moment from 'moment' import { Project, ProjectFlag } from 'common/types/responses' -import { getProtectedTags } from 'common/utils/getProtectedTags' +import { useProtectedTags } from 'common/utils/useProtectedTags' import { IonIcon } from '@ionic/react' import { warning } from 'ionicons/icons' import Tooltip from './Tooltip' @@ -14,8 +14,20 @@ type StaleFlagWarningType = { } const StaleFlagWarning: FC = ({ projectFlag }) => { - if (!Utils.getFlagsmithHasFeature('feature_versioning')) return null - const protectedTags = getProtectedTags(projectFlag, `${projectFlag.project}`) + const isFeatureVersioningEnabled = + Utils.getFlagsmithHasFeature('feature_versioning') + const protectedTags = useProtectedTags( + projectFlag, + `${projectFlag.project}`, + !isFeatureVersioningEnabled, + ) + + if (!isFeatureVersioningEnabled) return null + + if (protectedTags === undefined) { + return null + } + if (protectedTags?.length) { return null } diff --git a/frontend/web/components/import-export/FeatureExport.tsx b/frontend/web/components/import-export/FeatureExport.tsx index 988711d234d7..71d62bb01d50 100644 --- a/frontend/web/components/import-export/FeatureExport.tsx +++ b/frontend/web/components/import-export/FeatureExport.tsx @@ -9,7 +9,12 @@ import FeatureListStore from 'common/stores/feature-list-store' import FeatureListProvider from 'common/providers/FeatureListProvider' import AppActions from 'common/dispatcher/app-actions' import FeatureRow from 'components/FeatureRow' -import { FeatureState, ProjectFlag, TagStrategy } from 'common/types/responses' +import { + FeatureListProviderData, + FeatureState, + ProjectFlag, + TagStrategy, +} from 'common/types/responses' import ProjectStore from 'common/stores/project-store' import Utils from 'common/utils/utils' import Button from 'components/base/forms/Button' @@ -125,8 +130,8 @@ const FeatureExport: FC = ({ projectId }) => { environmentFlags, projectFlags, }: { - environmentFlags?: FeatureState[] - projectFlags: ProjectFlag[] + environmentFlags?: FeatureListProviderData['environmentFlags'] + projectFlags: FeatureListProviderData['projectFlags'] }) => { const isLoading = !FeatureListStore.hasLoaded @@ -159,11 +164,7 @@ const FeatureExport: FC = ({ projectId }) => { readOnly hideRemove hideAudit - descriptionInTooltip - hideActions - size='sm' environmentFlags={environmentFlags} - projectFlags={projectFlags} environmentId={environment} projectId={projectId} index={i} diff --git a/frontend/web/components/import-export/FeatureImport.tsx b/frontend/web/components/import-export/FeatureImport.tsx index f3cff83b460b..3dd6bc262e0a 100644 --- a/frontend/web/components/import-export/FeatureImport.tsx +++ b/frontend/web/components/import-export/FeatureImport.tsx @@ -134,16 +134,10 @@ const FeatureExport: FC = ({ projectId }) => { tags: tags?.length ? tags : undefined, }) }, [projectId, tags]) - const { - featureStates, - projectFlags, - }: { - projectFlags: ProjectFlag[] | null - featureStates: FeatureState[] | null - } = useMemo(() => { + const { featureStates, projectFlags } = useMemo(() => { if (fileData) { const createdDate = new Date().toISOString() - const existingFlags: ProjectFlag[] = + const existingFlags = !!fileData && !!currentFeatureStates && currentProjectflags?.map((projectFlag, i) => { @@ -426,9 +420,7 @@ const FeatureExport: FC = ({ projectId }) => { {