diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanel.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanel.tsx
new file mode 100644
index 0000000000..7c31209f16
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanel.tsx
@@ -0,0 +1,61 @@
+import { styled } from '@mui/material';
+import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
+import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import { FeatureOverviewSidePanelEnvironmentSwitches } from './FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitches';
+
+const StyledContainer = styled('div')(({ theme }) => ({
+ borderRadius: theme.shape.borderRadiusLarge,
+ backgroundColor: theme.palette.background.paper,
+ display: 'flex',
+ flexDirection: 'column',
+ padding: '1.5rem',
+ maxWidth: '350px',
+ minWidth: '350px',
+ marginRight: '1rem',
+ marginTop: '1rem',
+ [theme.breakpoints.down(1000)]: {
+ marginBottom: '1rem',
+ width: '100%',
+ maxWidth: 'none',
+ minWidth: 'auto',
+ },
+}));
+
+const StyledHeader = styled('h3')(({ theme }) => ({
+ display: 'flex',
+ gap: theme.spacing(1),
+ alignItems: 'center',
+ fontSize: theme.fontSizes.bodySize,
+ margin: 0,
+ marginBottom: theme.spacing(3),
+
+ // Make the help icon align with the text.
+ '& > :last-child': {
+ position: 'relative',
+ top: 1,
+ },
+}));
+
+export const FeatureOverviewSidePanel = () => {
+ const projectId = useRequiredPathParam('projectId');
+ const featureId = useRequiredPathParam('featureId');
+ const { feature } = useFeature(projectId, featureId);
+
+ return (
+
+
+ Enabled in environments ({feature.environments.length})
+
+
+ }
+ feature={feature}
+ />
+
+ );
+};
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitch/FeatureOverviewSidePanelEnvironmentSwitch.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitch/FeatureOverviewSidePanelEnvironmentSwitch.tsx
new file mode 100644
index 0000000000..0b6e3e3f97
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitch/FeatureOverviewSidePanelEnvironmentSwitch.tsx
@@ -0,0 +1,146 @@
+import { ENVIRONMENT_STRATEGY_ERROR } from 'constants/apiErrors';
+import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
+import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
+import useToast from 'hooks/useToast';
+import { IFeatureEnvironment } from 'interfaces/featureToggle';
+import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
+import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
+import { formatUnknownError } from 'utils/formatUnknownError';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
+import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
+import { UpdateEnabledMessage } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage';
+import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
+import { styled } from '@mui/material';
+import StringTruncator from 'component/common/StringTruncator/StringTruncator';
+
+const StyledContainer = styled('div')(({ theme }) => ({
+ '&:not(:last-of-type)': {
+ marginBottom: theme.spacing(2),
+ },
+}));
+
+const StyledLabel = styled('label')(({ theme }) => ({
+ display: 'inline-flex',
+ alignItems: 'center',
+ cursor: 'pointer',
+}));
+
+interface IFeatureOverviewSidePanelEnvironmentSwitchProps {
+ env: IFeatureEnvironment;
+ callback?: () => void;
+ showInfoBox: () => void;
+ children?: React.ReactNode;
+}
+
+export const FeatureOverviewSidePanelEnvironmentSwitch = ({
+ env,
+ callback,
+ showInfoBox,
+ children,
+}: IFeatureOverviewSidePanelEnvironmentSwitchProps) => {
+ const projectId = useRequiredPathParam('projectId');
+ const featureId = useRequiredPathParam('featureId');
+ const { toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } =
+ useFeatureApi();
+ const { refetchFeature } = useFeature(projectId, featureId);
+ const { setToastData, setToastApiError } = useToast();
+ const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
+ const {
+ onChangeRequestToggle,
+ onChangeRequestToggleClose,
+ onChangeRequestToggleConfirm,
+ changeRequestDialogDetails,
+ } = useChangeRequestToggle(projectId);
+
+ const handleToggleEnvironmentOn = async () => {
+ try {
+ await toggleFeatureEnvironmentOn(projectId, featureId, env.name);
+ setToastData({
+ type: 'success',
+ title: `Available in ${env.name}`,
+ text: `${featureId} is now available in ${env.name} based on its defined strategies.`,
+ });
+ refetchFeature();
+ if (callback) {
+ callback();
+ }
+ } catch (error: unknown) {
+ if (
+ error instanceof Error &&
+ error.message === ENVIRONMENT_STRATEGY_ERROR
+ ) {
+ showInfoBox();
+ } else {
+ setToastApiError(formatUnknownError(error));
+ }
+ }
+ };
+
+ const handleToggleEnvironmentOff = async () => {
+ try {
+ await toggleFeatureEnvironmentOff(projectId, featureId, env.name);
+ setToastData({
+ type: 'success',
+ title: `Unavailable in ${env.name}`,
+ text: `${featureId} is unavailable in ${env.name} and its strategies will no longer have any effect.`,
+ });
+ refetchFeature();
+ if (callback) {
+ callback();
+ }
+ } catch (error: unknown) {
+ setToastApiError(formatUnknownError(error));
+ }
+ };
+
+ const toggleEnvironment = async (e: React.ChangeEvent) => {
+ if (isChangeRequestConfigured(env.name)) {
+ e.preventDefault();
+ onChangeRequestToggle(featureId, env.name, !env.enabled);
+ return;
+ }
+ if (env.enabled) {
+ await handleToggleEnvironmentOff();
+ return;
+ }
+ await handleToggleEnvironmentOn();
+ };
+
+ const defaultContent = (
+ <>
+ {' '}
+
{env.enabled ? 'enabled' : 'disabled'} in
+
+
+ >
+ );
+
+ return (
+
+
+
+ {children ?? defaultContent}
+
+
+ }
+ />
+
+ );
+};
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitches.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitches.tsx
new file mode 100644
index 0000000000..e64f5c82d1
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitches.tsx
@@ -0,0 +1,94 @@
+import EnvironmentStrategyDialog from 'component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog';
+import { IFeatureToggle } from 'interfaces/featureToggle';
+import { useState } from 'react';
+import { FeatureOverviewSidePanelEnvironmentSwitch } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitch/FeatureOverviewSidePanelEnvironmentSwitch';
+import { Link, styled } from '@mui/material';
+import { Link as RouterLink } from 'react-router-dom';
+
+const StyledContainer = styled('div')(({ theme }) => ({
+ display: 'flex',
+ flexDirection: 'column',
+}));
+
+const StyledLabel = styled('p')(({ theme }) => ({
+ fontSize: theme.fontSizes.smallBody,
+}));
+
+const StyledSubLabel = styled('p')(({ theme }) => ({
+ fontSize: theme.fontSizes.smallerBody,
+ color: theme.palette.text.secondary,
+}));
+
+const StyledLink = styled(Link
)(() => ({
+ '&:hover, &:focus': {
+ textDecoration: 'underline',
+ },
+}));
+
+interface IFeatureOverviewSidePanelEnvironmentSwitchesProps {
+ feature: IFeatureToggle;
+ header: React.ReactNode;
+}
+
+export const FeatureOverviewSidePanelEnvironmentSwitches = ({
+ feature,
+ header,
+}: IFeatureOverviewSidePanelEnvironmentSwitchesProps) => {
+ const [showInfoBox, setShowInfoBox] = useState(false);
+ const [environmentName, setEnvironmentName] = useState('');
+
+ return (
+ <>
+ {header}
+ {feature.environments.map(environment => {
+ const strategiesLabel =
+ environment.strategies.length === 1
+ ? '1 strategy'
+ : `${environment.strategies.length} strategies`;
+
+ const variants = environment.variants ?? [];
+
+ const variantsLink = variants.length > 0 && (
+ <>
+ {' - '}
+
+ {variants.length === 1
+ ? '1 variant'
+ : `${variants.length} variants`}
+
+ >
+ );
+
+ return (
+ {
+ setEnvironmentName(environment.name);
+ setShowInfoBox(true);
+ }}
+ >
+
+ {environment.name}
+
+ {strategiesLabel}
+ {variantsLink}
+
+
+
+ );
+ })}
+ setShowInfoBox(false)}
+ projectId={feature.project}
+ featureId={feature.name}
+ environmentName={environmentName}
+ />
+ >
+ );
+};