mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
https://linear.app/unleash/issue/2-3911/address-new-ux-feedback Addresses UX feedback regarding new "add strategy" modal: - "View more strategies" instead of "View more" - Avoid horizontal scroll - Fix responsiveness - Prevent flicker when navigating between strategy and preview modals
353 lines
14 KiB
TypeScript
353 lines
14 KiB
TypeScript
import type React from 'react';
|
|
import { useEffect, useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import PermissionButton, {
|
|
type IPermissionButtonProps,
|
|
} from 'component/common/PermissionButton/PermissionButton';
|
|
import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
|
import { Box, Dialog, IconButton, styled, Typography } from '@mui/material';
|
|
import { LegacyFeatureStrategyMenuCards } from './LegacyFeatureStrategyMenuCards/LegacyFeatureStrategyMenuCards.tsx';
|
|
import { formatCreateStrategyPath } from '../FeatureStrategyCreate/FeatureStrategyCreate.tsx';
|
|
import MoreVert from '@mui/icons-material/MoreVert';
|
|
import CloseIcon from '@mui/icons-material/Close';
|
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
|
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
|
|
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
|
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
|
|
import useToast from 'hooks/useToast';
|
|
import { useReleasePlansApi } from 'hooks/api/actions/useReleasePlansApi/useReleasePlansApi';
|
|
import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans';
|
|
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|
import { LegacyReleasePlanReviewDialog } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/LegacyReleasePlanReviewDialog.tsx';
|
|
import { ReleasePlanPreview } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanPreview.tsx';
|
|
import {
|
|
FeatureStrategyMenuCards,
|
|
type StrategyFilterValue,
|
|
} from './FeatureStrategyMenuCards/FeatureStrategyMenuCards.tsx';
|
|
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
|
|
|
interface IFeatureStrategyMenuProps {
|
|
label: string;
|
|
projectId: string;
|
|
featureId: string;
|
|
environmentId: string;
|
|
variant?: IPermissionButtonProps['variant'];
|
|
matchWidth?: boolean;
|
|
disableReason?: string;
|
|
}
|
|
|
|
const StyledStrategyMenu = styled('div')(({ theme }) => ({
|
|
display: 'flex',
|
|
flexFlow: 'row',
|
|
justifyContent: 'flex-end',
|
|
gap: theme.spacing(1),
|
|
}));
|
|
|
|
const StyledAdditionalMenuButton = styled(PermissionButton)(({ theme }) => ({
|
|
minWidth: 0,
|
|
width: theme.spacing(4.5),
|
|
alignSelf: 'stretch',
|
|
paddingBlock: 0,
|
|
}));
|
|
|
|
const StyledHeader = styled(Box)(({ theme }) => ({
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
padding: theme.spacing(4, 4, 2, 4),
|
|
}));
|
|
|
|
export const FeatureStrategyMenu = ({
|
|
label,
|
|
projectId,
|
|
featureId,
|
|
environmentId,
|
|
variant,
|
|
matchWidth,
|
|
disableReason,
|
|
}: IFeatureStrategyMenuProps) => {
|
|
const [isStrategyMenuDialogOpen, setIsStrategyMenuDialogOpen] =
|
|
useState<boolean>(false);
|
|
const [onlyReleasePlans, setOnlyReleasePlans] = useState<boolean>(false);
|
|
const [filter, setFilter] = useState<StrategyFilterValue>(null);
|
|
const navigate = useNavigate();
|
|
const { trackEvent } = usePlausibleTracker();
|
|
const [selectedTemplate, setSelectedTemplate] =
|
|
useState<IReleasePlanTemplate>();
|
|
const [addReleasePlanOpen, setAddReleasePlanOpen] = useState(false);
|
|
const [releasePlanPreview, setReleasePlanPreview] = useState(false);
|
|
const dialogId = isStrategyMenuDialogOpen
|
|
? 'FeatureStrategyMenuDialog'
|
|
: undefined;
|
|
const { setToastApiError, setToastData } = useToast();
|
|
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
|
|
const { addChange } = useChangeRequestApi();
|
|
const { refetch: refetchChangeRequests } =
|
|
usePendingChangeRequests(projectId);
|
|
const { refetch } = useReleasePlans(projectId, featureId, environmentId);
|
|
const { addReleasePlanToFeature } = useReleasePlansApi();
|
|
const { isEnterprise } = useUiConfig();
|
|
const displayReleasePlanButton = isEnterprise();
|
|
const crProtected = isChangeRequestConfigured(environmentId);
|
|
const newStrategyModalEnabled = useUiFlag('newStrategyModal');
|
|
|
|
const onClose = () => {
|
|
setIsStrategyMenuDialogOpen(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (!isStrategyMenuDialogOpen) return;
|
|
setReleasePlanPreview(false);
|
|
}, [isStrategyMenuDialogOpen]);
|
|
|
|
const openDefaultStrategyCreationModal = (event: React.SyntheticEvent) => {
|
|
trackEvent('strategy-add', {
|
|
props: {
|
|
buttonTitle: label,
|
|
},
|
|
});
|
|
navigate(createStrategyPath);
|
|
};
|
|
|
|
const openMoreStrategies = (event: React.SyntheticEvent) => {
|
|
setOnlyReleasePlans(false);
|
|
setIsStrategyMenuDialogOpen(true);
|
|
};
|
|
|
|
const openReleasePlans = (event: React.SyntheticEvent) => {
|
|
setOnlyReleasePlans(true);
|
|
setIsStrategyMenuDialogOpen(true);
|
|
};
|
|
|
|
const addReleasePlan = async (template: IReleasePlanTemplate) => {
|
|
try {
|
|
if (crProtected) {
|
|
await addChange(projectId, environmentId, {
|
|
feature: featureId,
|
|
action: 'addReleasePlan',
|
|
payload: {
|
|
templateId: template.id,
|
|
},
|
|
});
|
|
|
|
setToastData({
|
|
type: 'success',
|
|
text: 'Added to draft',
|
|
});
|
|
|
|
refetchChangeRequests();
|
|
} else {
|
|
await addReleasePlanToFeature(
|
|
featureId,
|
|
template.id,
|
|
projectId,
|
|
environmentId,
|
|
);
|
|
|
|
setToastData({
|
|
type: 'success',
|
|
text: 'Release plan added',
|
|
});
|
|
|
|
refetch();
|
|
}
|
|
trackEvent('release-management', {
|
|
props: {
|
|
eventType: 'add-plan',
|
|
plan: template.name,
|
|
},
|
|
});
|
|
} catch (error: unknown) {
|
|
setToastApiError(formatUnknownError(error));
|
|
} finally {
|
|
setAddReleasePlanOpen(false);
|
|
setSelectedTemplate(undefined);
|
|
onClose();
|
|
}
|
|
};
|
|
|
|
const createStrategyPath = formatCreateStrategyPath(
|
|
projectId,
|
|
featureId,
|
|
environmentId,
|
|
'flexibleRollout',
|
|
true,
|
|
);
|
|
|
|
return (
|
|
<StyledStrategyMenu onClick={(event) => event.stopPropagation()}>
|
|
{newStrategyModalEnabled ? (
|
|
<PermissionButton
|
|
data-testid='ADD_STRATEGY_BUTTON'
|
|
permission={CREATE_FEATURE_STRATEGY}
|
|
projectId={projectId}
|
|
environmentId={environmentId}
|
|
onClick={openMoreStrategies}
|
|
aria-labelledby={dialogId}
|
|
variant={variant}
|
|
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
|
|
disabled={Boolean(disableReason)}
|
|
tooltipProps={{
|
|
title: disableReason ? disableReason : undefined,
|
|
}}
|
|
>
|
|
Add strategy
|
|
</PermissionButton>
|
|
) : (
|
|
<>
|
|
{displayReleasePlanButton ? (
|
|
<PermissionButton
|
|
data-testid='ADD_TEMPLATE_BUTTON'
|
|
permission={CREATE_FEATURE_STRATEGY}
|
|
projectId={projectId}
|
|
environmentId={environmentId}
|
|
onClick={openReleasePlans}
|
|
aria-labelledby={dialogId}
|
|
variant='outlined'
|
|
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
|
|
disabled={Boolean(disableReason)}
|
|
tooltipProps={{
|
|
title: disableReason
|
|
? disableReason
|
|
: undefined,
|
|
}}
|
|
>
|
|
Use template
|
|
</PermissionButton>
|
|
) : null}
|
|
|
|
<PermissionButton
|
|
data-testid='ADD_STRATEGY_BUTTON'
|
|
permission={CREATE_FEATURE_STRATEGY}
|
|
projectId={projectId}
|
|
environmentId={environmentId}
|
|
onClick={openDefaultStrategyCreationModal}
|
|
aria-labelledby={dialogId}
|
|
variant={variant}
|
|
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
|
|
disabled={Boolean(disableReason)}
|
|
tooltipProps={{
|
|
title: disableReason ? disableReason : undefined,
|
|
}}
|
|
>
|
|
{label}
|
|
</PermissionButton>
|
|
|
|
<StyledAdditionalMenuButton
|
|
permission={CREATE_FEATURE_STRATEGY}
|
|
projectId={projectId}
|
|
environmentId={environmentId}
|
|
onClick={openMoreStrategies}
|
|
variant='outlined'
|
|
hideLockIcon
|
|
disabled={Boolean(disableReason)}
|
|
tooltipProps={{
|
|
title: disableReason
|
|
? disableReason
|
|
: 'More strategies',
|
|
}}
|
|
>
|
|
<MoreVert />
|
|
</StyledAdditionalMenuButton>
|
|
</>
|
|
)}
|
|
<Dialog
|
|
open={isStrategyMenuDialogOpen}
|
|
onClose={onClose}
|
|
maxWidth='md'
|
|
PaperProps={{
|
|
sx: {
|
|
borderRadius: '12px',
|
|
height: newStrategyModalEnabled ? '100%' : 'auto',
|
|
width: '100%',
|
|
},
|
|
}}
|
|
>
|
|
{newStrategyModalEnabled ? (
|
|
<>
|
|
<StyledHeader>
|
|
<Typography variant='h2'>Add strategy</Typography>
|
|
<IconButton
|
|
size='small'
|
|
onClick={onClose}
|
|
edge='end'
|
|
aria-label='close'
|
|
>
|
|
<CloseIcon fontSize='small' />
|
|
</IconButton>
|
|
</StyledHeader>
|
|
{releasePlanPreview && selectedTemplate ? (
|
|
<ReleasePlanPreview
|
|
template={selectedTemplate}
|
|
projectId={projectId}
|
|
featureName={featureId}
|
|
environment={environmentId}
|
|
crProtected={crProtected}
|
|
onBack={() => setReleasePlanPreview(false)}
|
|
onConfirm={() => {
|
|
addReleasePlan(selectedTemplate);
|
|
}}
|
|
/>
|
|
) : (
|
|
<FeatureStrategyMenuCards
|
|
projectId={projectId}
|
|
featureId={featureId}
|
|
environmentId={environmentId}
|
|
filter={filter}
|
|
setFilter={setFilter}
|
|
onAddReleasePlan={(template) => {
|
|
setSelectedTemplate(template);
|
|
addReleasePlan(template);
|
|
}}
|
|
onReviewReleasePlan={(template) => {
|
|
setSelectedTemplate(template);
|
|
setReleasePlanPreview(true);
|
|
}}
|
|
onClose={onClose}
|
|
/>
|
|
)}
|
|
</>
|
|
) : (
|
|
<LegacyFeatureStrategyMenuCards
|
|
projectId={projectId}
|
|
featureId={featureId}
|
|
environmentId={environmentId}
|
|
onlyReleasePlans={onlyReleasePlans}
|
|
onAddReleasePlan={(template) => {
|
|
setSelectedTemplate(template);
|
|
addReleasePlan(template);
|
|
}}
|
|
onReviewReleasePlan={(template) => {
|
|
setSelectedTemplate(template);
|
|
setAddReleasePlanOpen(true);
|
|
onClose();
|
|
}}
|
|
onClose={onClose}
|
|
/>
|
|
)}
|
|
</Dialog>
|
|
{selectedTemplate && (
|
|
<LegacyReleasePlanReviewDialog
|
|
open={addReleasePlanOpen}
|
|
setOpen={(open) => {
|
|
setAddReleasePlanOpen(open);
|
|
if (!open) {
|
|
setIsStrategyMenuDialogOpen(true);
|
|
}
|
|
}}
|
|
onConfirm={() => {
|
|
addReleasePlan(selectedTemplate);
|
|
}}
|
|
template={selectedTemplate}
|
|
projectId={projectId}
|
|
featureName={featureId}
|
|
environment={environmentId}
|
|
crProtected={crProtected}
|
|
/>
|
|
)}
|
|
</StyledStrategyMenu>
|
|
);
|
|
};
|