1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-17 01:17:29 +02:00

feat: release plan review dialogue (#9712)

This commit is contained in:
Jaanus Sellin 2025-04-08 12:24:09 +03:00 committed by GitHub
parent 1930c0f408
commit 6c74c994aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 192 additions and 23 deletions

View File

@ -68,7 +68,7 @@ const StyledTopRow = styled('div')(({ theme }) => ({
interface IFeatureReleasePlanCardProps {
template: IReleasePlanTemplate;
onClick: () => void;
onPreviewClick?: (e: React.MouseEvent) => void;
onPreviewClick: (e: React.MouseEvent) => void;
}
export const FeatureReleasePlanCard = ({
@ -85,9 +85,7 @@ export const FeatureReleasePlanCard = ({
const handlePreviewClick = (e: React.MouseEvent) => {
e.stopPropagation();
if (onPreviewClick) {
onPreviewClick(e);
}
onPreviewClick(e);
};
return (

View File

@ -14,7 +14,6 @@ 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 { ReleasePlanAddDialog } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanAddDialog';
import { useReleasePlansApi } from 'hooks/api/actions/useReleasePlansApi/useReleasePlansApi';
import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
@ -22,6 +21,8 @@ import { formatUnknownError } from 'utils/formatUnknownError';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { OldFeatureStrategyMenuCards } from './FeatureStrategyMenuCards/OldFeatureStrategyMenuCards';
import { ReleasePlanReviewDialog } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanReviewDialog';
import { ReleasePlanAddDialog } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanAddDialog';
interface IFeatureStrategyMenuProps {
label: string;
@ -104,16 +105,14 @@ export const FeatureStrategyMenu = ({
setAnchor(event.currentTarget);
};
const addReleasePlan = async () => {
if (!selectedTemplate) return;
const addReleasePlan = async (template: IReleasePlanTemplate) => {
try {
if (crProtected) {
await addChange(projectId, environmentId, {
feature: featureId,
action: 'addReleasePlan',
payload: {
templateId: selectedTemplate.id,
templateId: template.id,
},
});
@ -126,7 +125,7 @@ export const FeatureStrategyMenu = ({
} else {
await addReleasePlanToFeature(
featureId,
selectedTemplate.id,
template.id,
projectId,
environmentId,
);
@ -141,7 +140,7 @@ export const FeatureStrategyMenu = ({
trackEvent('release-management', {
props: {
eventType: 'add-plan',
plan: selectedTemplate.name,
plan: template.name,
},
});
} catch (error: unknown) {
@ -240,6 +239,10 @@ export const FeatureStrategyMenu = ({
environmentId={environmentId}
onlyReleasePlans={onlyReleasePlans}
onAddReleasePlan={(template) => {
setSelectedTemplate(template);
addReleasePlan(template);
}}
onReviewReleasePlan={(template) => {
setSelectedTemplate(template);
setAddReleasePlanOpen(true);
}}
@ -258,18 +261,33 @@ export const FeatureStrategyMenu = ({
/>
)}
</Popover>
{selectedTemplate && (
<ReleasePlanAddDialog
open={addReleasePlanOpen}
setOpen={setAddReleasePlanOpen}
onConfirm={addReleasePlan}
template={selectedTemplate}
projectId={projectId}
featureName={featureId}
environment={environmentId}
crProtected={crProtected}
/>
)}
{selectedTemplate &&
(newStrategyDropdownEnabled ? (
<ReleasePlanReviewDialog
open={addReleasePlanOpen}
setOpen={setAddReleasePlanOpen}
onConfirm={() => {
addReleasePlan(selectedTemplate);
}}
template={selectedTemplate}
projectId={projectId}
featureName={featureId}
environment={environmentId}
crProtected={crProtected}
/>
) : (
<ReleasePlanAddDialog
open={addReleasePlanOpen}
setOpen={setAddReleasePlanOpen}
onConfirm={() => {
addReleasePlan(selectedTemplate);
}}
template={selectedTemplate}
projectId={projectId}
featureName={featureId}
environment={environmentId}
/>
))}
</StyledStrategyMenu>
);
};

View File

@ -23,6 +23,7 @@ interface IFeatureStrategyMenuCardsProps {
environmentId: string;
onlyReleasePlans: boolean;
onAddReleasePlan: (template: IReleasePlanTemplate) => void;
onReviewReleasePlan: (template: IReleasePlanTemplate) => void;
onClose?: () => void;
}
@ -141,6 +142,7 @@ export const FeatureStrategyMenuCards = ({
environmentId,
onlyReleasePlans,
onAddReleasePlan,
onReviewReleasePlan,
onClose,
}: IFeatureStrategyMenuCardsProps) => {
const { strategies } = useStrategies();
@ -239,6 +241,9 @@ export const FeatureStrategyMenuCards = ({
onClick={() =>
onAddReleasePlan(template)
}
onPreviewClick={() =>
onReviewReleasePlan(template)
}
/>
</CardWrapper>
))}

View File

@ -0,0 +1,148 @@
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
import { ReleasePlan } from './ReleasePlan';
import { useReleasePlanPreview } from 'hooks/useReleasePlanPreview';
import {
styled,
Typography,
Alert,
Box,
IconButton,
Dialog,
DialogContent,
DialogActions,
Button,
} from '@mui/material';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import CloseIcon from '@mui/icons-material/Close';
const StyledDialog = styled(Dialog)(({ theme }) => ({
'& .MuiDialog-paper': {
borderRadius: theme.shape.borderRadiusLarge,
maxWidth: theme.spacing(85),
},
}));
const StyledDialogActions = styled(DialogActions)(({ theme }) => ({
padding: theme.spacing(2, 4, 4),
}));
const TopRow = styled(Box)(({ theme }) => ({
display: 'flex',
justifyContent: 'space-between',
marginBottom: theme.spacing(2),
}));
const BackButton = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
}));
const StyledBackIcon = styled(ArrowBackIcon)(({ theme }) => ({
marginRight: theme.spacing(1),
color: theme.palette.primary.main,
display: 'flex',
alignSelf: 'center',
}));
const BackText = styled(Typography)(({ theme }) => ({
fontWeight: theme.typography.fontWeightMedium,
display: 'flex',
alignItems: 'center',
lineHeight: 1,
}));
interface IReleasePlanAddDialogProps {
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
onConfirm: () => void;
template: IReleasePlanTemplate;
projectId: string;
featureName: string;
environment: string;
crProtected?: boolean;
}
export const ReleasePlanReviewDialog = ({
open,
setOpen,
onConfirm,
template,
projectId,
featureName,
environment,
crProtected,
}: IReleasePlanAddDialogProps) => {
const { feature } = useFeature(projectId, featureName);
const { releasePlans } = useReleasePlans(
projectId,
featureName,
environment,
);
const activeReleasePlan = releasePlans[0];
const environmentData = feature?.environments.find(
({ name }) => name === environment,
);
const environmentEnabled = environmentData?.enabled;
const planPreview = useReleasePlanPreview(
template.id,
featureName,
environment,
);
const handleClose = () => setOpen(false);
return (
<StyledDialog open={open} onClose={handleClose} fullWidth maxWidth='md'>
<DialogContent>
<TopRow>
<BackButton onClick={handleClose}>
<StyledBackIcon />
<BackText variant='body2' color='primary'>
Go back
</BackText>
</BackButton>
<IconButton
size='small'
onClick={handleClose}
edge='end'
aria-label='close'
>
<CloseIcon fontSize='small' />
</IconButton>
</TopRow>
{activeReleasePlan && (
<Alert severity='error' sx={{ mb: 1 }}>
This feature environment currently has{' '}
<strong>{activeReleasePlan.name}</strong> -{' '}
<strong>{activeReleasePlan.milestones[0].name}</strong>
{environmentEnabled ? ' running' : ' paused'}. Adding a
new release plan will replace the existing release plan.
</Alert>
)}
<div>
<ReleasePlan plan={planPreview} readonly />
</div>
{crProtected && (
<Typography sx={{ mt: 4 }}>
<strong>Adding</strong> release template{' '}
<strong>{template?.name}</strong> to{' '}
<strong>{featureName}</strong> in{' '}
<strong>{environment}</strong>.
</Typography>
)}
</DialogContent>
<StyledDialogActions>
<Button variant='contained' color='primary' onClick={onConfirm}>
{crProtected ? 'Add suggestion to draft' : 'Use template'}
</Button>
</StyledDialogActions>
</StyledDialog>
);
};