import { useHistory, useParams } from 'react-router-dom'; import useFeature from '../../../../../hooks/api/getters/useFeature/useFeature'; import { useStyles } from './FeatureStrategiesEnvironments.styles'; import { Tabs, Tab, Button, useMediaQuery } from '@material-ui/core'; import TabPanel from '../../../../common/TabNav/TabPanel'; import useTabs from '../../../../../hooks/useTabs'; import FeatureStrategiesEnvironmentList from './FeatureStrategiesEnvironmentList/FeatureStrategiesEnvironmentList'; import { useContext, useEffect, useState } from 'react'; import FeatureStrategiesUIContext from '../../../../../contexts/FeatureStrategiesUIContext'; import ConditionallyRender from '../../../../common/ConditionallyRender'; import FeatureStrategiesConfigure from './FeatureStrategiesConfigure/FeatureStrategiesConfigure'; import classNames from 'classnames'; import useToast from '../../../../../hooks/useToast'; import { IFeatureViewParams } from '../../../../../interfaces/params'; import cloneDeep from 'lodash.clonedeep'; import FeatureStrategiesRefresh from './FeatureStrategiesRefresh/FeatureStrategiesRefresh'; import FeatureEnvironmentStrategyExecution from './FeatureEnvironmentStrategyExecution/FeatureEnvironmentStrategyExecution'; import { ADD_NEW_STRATEGY_ID } from '../../../../../testIds'; import NoItems from '../../../../common/NoItems/NoItems'; import ResponsiveButton from '../../../../common/ResponsiveButton/ResponsiveButton'; import { Add } from '@material-ui/icons'; import AccessContext from '../../../../../contexts/AccessContext'; import { UPDATE_FEATURE } from '../../../../AccessProvider/permissions'; import useQueryParams from '../../../../../hooks/useQueryParams'; const FeatureStrategiesEnvironments = () => { const smallScreen = useMediaQuery('(max-width:700px)'); const { hasAccess } = useContext(AccessContext); const history = useHistory(); const startingTabId = 0; const { projectId, featureId } = useParams(); const { toast, setToastData } = useToast(); const [showRefreshPrompt, setShowRefreshPrompt] = useState(false); const styles = useStyles(); const query = useQueryParams(); const addStrategy = query.get('addStrategy'); const environmentTab = query.get('environment'); const { a11yProps, activeTabIdx, setActiveTab } = useTabs(startingTabId); const { setActiveEnvironment, configureNewStrategy, expandedSidebar, setExpandedSidebar, featureCache, setFeatureCache, } = useContext(FeatureStrategiesUIContext); const { feature } = useFeature(projectId, featureId, { revalidateOnFocus: false, revalidateIfStale: false, revalidateOnReconnect: false, refreshInterval: 5000, }); useEffect(() => { if (addStrategy) { setExpandedSidebar(true); } if (!feature) return; if (environmentTab) { const env = feature.environments.find( env => env.name === environmentTab ); const index = feature.environments.findIndex( env => env.name === environmentTab ); if (index < 0 || !env) return; setActiveEnvironment(env); setActiveTab(index); return; } if (feature?.environments?.length === 0) return; setActiveEnvironment(feature?.environments[activeTabIdx]); /*eslint-disable-next-line */ }, [feature]); useEffect(() => { if (!feature) return; if (featureCache === null || !featureCache.createdAt) { setFeatureCache(cloneDeep(feature)); } /* eslint-disable-next-line */ }, [feature]); useEffect(() => { if (!feature) return; if (featureCache === null) return; if (!featureCache.createdAt) return; const equal = compareCacheToFeature(); if (!equal) { setShowRefreshPrompt(true); } /*eslint-disable-next-line */ }, [feature]); if (!feature) return null; const renderTabs = () => { return featureCache?.environments?.map((env, index) => { return ( setActiveTab(index)} className={styles.tabButton} /> ); }); }; const compareCacheToFeature = () => { let equal = true; // If the length of environments are different if (!featureCache) return false; if ( feature?.environments?.length !== featureCache?.environments?.length ) { equal = false; } feature?.environments?.forEach(env => { const cachedEnv = featureCache?.environments?.find( cacheEnv => cacheEnv.name === env.name ); if (!cachedEnv) { equal = false; return; } // If displayName is different if (env?.displayName !== cachedEnv?.displayName) { equal = false; return; } // If the type of environments are different if (env?.type !== cachedEnv?.type) { equal = false; return; } }); if (!equal) return equal; feature?.environments?.forEach(env => { const cachedEnv = featureCache?.environments?.find( cachedEnv => cachedEnv.name === env.name ); if (!cachedEnv) return; if (cachedEnv.strategies.length !== env.strategies.length) { equal = false; return; } env?.strategies?.forEach(strategy => { const cachedStrategy = cachedEnv?.strategies?.find( cachedStrategy => cachedStrategy.id === strategy.id ); // Check stickiness if (cachedStrategy?.stickiness !== strategy?.stickiness) { equal = false; return; } if (cachedStrategy?.groupId !== strategy?.groupId) { equal = false; return; } // Check groupId const cacheParamKeys = Object.keys(cachedStrategy?.parameters || {}); const strategyParamKeys = Object.keys(strategy?.parameters || {}); // Check length of parameters if (cacheParamKeys.length !== strategyParamKeys.length) { equal = false; return; } // Make sure parameters are the same strategyParamKeys.forEach(key => { const found = cacheParamKeys.find( cacheKey => cacheKey === key ); if (!found) { equal = false; return; } }); // Check value of parameters strategyParamKeys.forEach(key => { const strategyValue = strategy.parameters[key]; const cachedValue = cachedStrategy.parameters[key]; if (strategyValue !== cachedValue) { equal = false; return; } }); // Check length of constraints const cachedConstraints = cachedStrategy.constraints; const strategyConstraints = strategy.constraints; if (cachedConstraints.length !== strategyConstraints.length) { equal = false; return; } // Check constraints -> are we g uaranteed that constraints will occur in the same order each time? }); }); return equal; // If the parameter values are different // If the constraint length is different // If the constraint operators are different // If the constraint values are different // If the stickiness is different // If the groupId is different }; const renderTabPanels = () => { const tabContentClasses = classNames(styles.tabContentContainer, { [styles.containerListView]: configureNewStrategy, }); const listContainerClasses = classNames(styles.listContainer, { [styles.listContainerFullWidth]: expandedSidebar, [styles.listContainerWithoutSidebar]: !hasAccess(UPDATE_FEATURE), }); return featureCache?.environments?.map((env, index) => { return (
0 || expandedSidebar } show={ <>
} /> } elseShow={

No strategies added in the{' '} {env.name} environment

Strategies added in this environment will only be executed if the SDK is using an API key configured for this environment. Read more here

{ setExpandedSidebar( prev => !prev ); }} > Add your first strategy } /> } /> } />
); }); }; const handleRefresh = () => { setFeatureCache(cloneDeep(feature)); setShowRefreshPrompt(false); }; const handleCancel = () => { setShowRefreshPrompt(false); }; const classes = classNames(styles.container, { [styles.fullWidth]: !expandedSidebar, }); return (

Environments

setExpandedSidebar(prev => !prev) } Icon={Add} maxWidth="700px" permission={UPDATE_FEATURE} > Add new strategy } />
{ setActiveTab(tabId); setActiveEnvironment( featureCache?.environments[tabId] ); history.replace(history.location.pathname); }} indicatorColor="primary" textColor="primary" className={styles.tabNavigation} > {renderTabs()}
{renderTabPanels()} } />
{toast} } />
); }; export default FeatureStrategiesEnvironments;