1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-11-24 20:06:55 +01:00

refactor: simplify components

This commit is contained in:
FredrikOseberg 2025-10-21 14:07:19 +02:00
parent d9503de15c
commit 5869c7e04f
No known key found for this signature in database
GPG Key ID: 282FD8A6D8F9BCF0
4 changed files with 87 additions and 148 deletions

View File

@ -340,15 +340,12 @@ const CreateMilestoneProgression: FC<{
<MilestoneAutomationSection status={status}>
<MilestoneTransitionDisplay
intervalMinutes={milestone.transitionCondition.intervalMinutes}
onSave={async (payload) => {
onUpdateChangeRequestSubmit?.(milestone.id, payload);
}}
onDelete={() => onDeleteChangeRequestSubmit?.(milestone.id)}
milestoneName={milestone.name}
status={status}
projectId={projectId}
environment={environmentName}
featureName={featureName}
sourceMilestoneId={milestone.id}
onUpdate={onUpdate || (() => {})}
onChangeRequestSubmit={onUpdateChangeRequestSubmit}
hasPendingUpdate={false}
hasPendingDelete={false}
/>
@ -467,15 +464,12 @@ const UpdateMilestoneProgression: FC<{
<MilestoneAutomationSection status={status}>
<MilestoneTransitionDisplay
intervalMinutes={milestone.transitionCondition.intervalMinutes}
onSave={async (payload) => {
onUpdateChangeRequestSubmit?.(milestone.id, payload);
}}
onDelete={() => onDeleteChangeRequestSubmit?.(milestone.id)}
milestoneName={milestone.name}
status={status}
projectId={projectId}
environment={environmentName}
featureName={featureName}
sourceMilestoneId={milestone.id}
onUpdate={onUpdate || (() => {})}
onChangeRequestSubmit={onUpdateChangeRequestSubmit}
hasPendingUpdate={false}
hasPendingDelete={false}
/>
@ -682,15 +676,12 @@ const ConsolidatedProgressionChanges: FC<{
<MilestoneAutomationSection status={status}>
<MilestoneTransitionDisplay
intervalMinutes={displayMilestone.transitionCondition.intervalMinutes}
onSave={async (payload) => {
onUpdateChangeRequestSubmit?.(displayMilestone.id, payload);
}}
onDelete={() => onDeleteChangeRequestSubmit?.(displayMilestone.id)}
milestoneName={displayMilestone.name}
status={status}
projectId={projectId}
environment={environmentName}
featureName={featureName}
sourceMilestoneId={displayMilestone.id}
onUpdate={onUpdate || (() => {})}
onChangeRequestSubmit={onUpdateChangeRequestSubmit}
hasPendingUpdate={false}
hasPendingDelete={Boolean(deleteChange)}
/>

View File

@ -1,12 +1,7 @@
import { useState } from 'react';
import { Button, styled } from '@mui/material';
import BoltIcon from '@mui/icons-material/Bolt';
import { useMilestoneProgressionForm } from '../hooks/useMilestoneProgressionForm.js';
import { useMilestoneProgressionsApi } from 'hooks/api/actions/useMilestoneProgressionsApi/useMilestoneProgressionsApi';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import { MilestoneProgressionTimeInput } from './MilestoneProgressionTimeInput.tsx';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import type { CreateMilestoneProgressionSchema } from 'openapi';
const StyledFormContainer = styled('div')(({ theme }) => ({
@ -60,74 +55,27 @@ const StyledErrorMessage = styled('span')(({ theme }) => ({
interface IMilestoneProgressionFormProps {
sourceMilestoneId: string;
targetMilestoneId: string;
projectId: string;
environment: string;
featureName: string;
onSave: () => void;
onSubmit: (payload: CreateMilestoneProgressionSchema) => Promise<void>;
onCancel: () => void;
onChangeRequestSubmit?: (
progressionPayload: CreateMilestoneProgressionSchema,
) => void;
}
export const MilestoneProgressionForm = ({
sourceMilestoneId,
targetMilestoneId,
projectId,
environment,
featureName,
onSave,
onSubmit,
onCancel,
onChangeRequestSubmit,
}: IMilestoneProgressionFormProps) => {
const form = useMilestoneProgressionForm(
sourceMilestoneId,
targetMilestoneId,
);
const { createMilestoneProgression } = useMilestoneProgressionsApi();
const { setToastData, setToastApiError } = useToast();
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChangeRequestSubmit = () => {
const progressionPayload = form.getProgressionPayload();
onChangeRequestSubmit?.(progressionPayload);
};
const handleDirectSubmit = async () => {
setIsSubmitting(true);
try {
await createMilestoneProgression(
projectId,
environment,
featureName,
form.getProgressionPayload(),
);
setToastData({
type: 'success',
text: 'Automation configured successfully',
});
onSave();
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
} finally {
setIsSubmitting(false);
}
};
const handleSubmit = async () => {
if (isSubmitting) return;
if (!form.validate()) {
return;
}
if (isChangeRequestConfigured(environment) && onChangeRequestSubmit) {
handleChangeRequestSubmit();
} else {
await handleDirectSubmit();
}
await onSubmit(form.getProgressionPayload());
};
const handleKeyDown = (event: React.KeyboardEvent) => {
@ -150,7 +98,6 @@ export const MilestoneProgressionForm = ({
timeUnit={form.timeUnit}
onTimeValueChange={form.handleTimeValueChange}
onTimeUnitChange={form.handleTimeUnitChange}
disabled={isSubmitting}
/>
</StyledTopRow>
<StyledButtonGroup>
@ -161,7 +108,6 @@ export const MilestoneProgressionForm = ({
variant='outlined'
onClick={onCancel}
size='small'
disabled={isSubmitting}
>
Cancel
</Button>
@ -170,9 +116,8 @@ export const MilestoneProgressionForm = ({
color='primary'
onClick={handleSubmit}
size='small'
disabled={isSubmitting}
>
{isSubmitting ? 'Saving...' : 'Save'}
Save
</Button>
</StyledButtonGroup>
</StyledFormContainer>

View File

@ -3,16 +3,11 @@ import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import { Button, IconButton, styled } from '@mui/material';
import { Badge } from 'component/common/Badge/Badge';
import type { MilestoneStatus } from './ReleasePlanMilestoneStatus.tsx';
import { useState } from 'react';
import { useMilestoneProgressionsApi } from 'hooks/api/actions/useMilestoneProgressionsApi/useMilestoneProgressionsApi';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import { MilestoneProgressionTimeInput } from '../MilestoneProgressionForm/MilestoneProgressionTimeInput.tsx';
import {
useMilestoneProgressionForm,
getTimeValueAndUnitFromMinutes,
} from '../hooks/useMilestoneProgressionForm.js';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import type { UpdateMilestoneProgressionSchema } from 'openapi';
const StyledDisplayContainer = styled('div')(({ theme }) => ({
@ -62,50 +57,32 @@ const StyledButtonGroup = styled('div')(({ theme }) => ({
interface IMilestoneTransitionDisplayProps {
intervalMinutes: number;
onSave: (payload: UpdateMilestoneProgressionSchema) => Promise<void>;
onDelete: () => void;
milestoneName: string;
status?: MilestoneStatus;
projectId: string;
environment: string;
featureName: string;
sourceMilestoneId: string;
onUpdate: () => void;
onChangeRequestSubmit?: (
sourceMilestoneId: string,
payload: UpdateMilestoneProgressionSchema,
) => void;
hasPendingUpdate?: boolean;
hasPendingDelete?: boolean;
}
export const MilestoneTransitionDisplay = ({
intervalMinutes,
onSave,
onDelete,
milestoneName,
status,
projectId,
environment,
featureName,
sourceMilestoneId,
onUpdate,
onChangeRequestSubmit,
hasPendingUpdate = false,
hasPendingDelete = false,
}: IMilestoneTransitionDisplayProps) => {
const { updateMilestoneProgression } = useMilestoneProgressionsApi();
const { setToastData, setToastApiError } = useToast();
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
const initial = getTimeValueAndUnitFromMinutes(intervalMinutes);
const form = useMilestoneProgressionForm(
sourceMilestoneId,
sourceMilestoneId, // We don't need targetMilestone for edit, just reuse source
'', // sourceMilestoneId not needed for display
'', // targetMilestoneId not needed for display
{
timeValue: initial.value,
timeUnit: initial.unit,
},
);
const [isSubmitting, setIsSubmitting] = useState(false);
const currentIntervalMinutes = form.getIntervalMinutes();
const hasChanged = currentIntervalMinutes !== intervalMinutes;
@ -113,7 +90,7 @@ export const MilestoneTransitionDisplay = ({
const showDraftBadge = hasPendingUpdate || hasPendingDelete;
const handleSave = async () => {
if (isSubmitting || !hasChanged) return;
if (!hasChanged) return;
const payload: UpdateMilestoneProgressionSchema = {
transitionCondition: {
@ -121,32 +98,9 @@ export const MilestoneTransitionDisplay = ({
},
};
if (isChangeRequestConfigured(environment) && onChangeRequestSubmit) {
onChangeRequestSubmit(sourceMilestoneId, payload);
// Reset the form after submitting to change request
handleReset();
return;
}
setIsSubmitting(true);
try {
await updateMilestoneProgression(
projectId,
environment,
featureName,
sourceMilestoneId,
payload,
);
setToastData({
type: 'success',
text: 'Automation updated successfully',
});
onUpdate();
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
} finally {
setIsSubmitting(false);
}
await onSave(payload);
// Reset the form after save
handleReset();
};
const handleReset = () => {
@ -177,7 +131,6 @@ export const MilestoneTransitionDisplay = ({
timeUnit={form.timeUnit}
onTimeValueChange={form.handleTimeValueChange}
onTimeUnitChange={form.handleTimeUnitChange}
disabled={isSubmitting}
/>
</StyledContentGroup>
<StyledButtonGroup>
@ -187,9 +140,8 @@ export const MilestoneTransitionDisplay = ({
color='primary'
onClick={handleSave}
size='small'
disabled={isSubmitting}
>
{isSubmitting ? 'Saving...' : 'Save'}
Save
</Button>
)}
{showDraftBadge && (
@ -202,7 +154,6 @@ export const MilestoneTransitionDisplay = ({
size='small'
aria-label={`Delete automation for ${milestoneName}`}
sx={{ padding: 0.5 }}
disabled={isSubmitting}
>
<DeleteOutlineIcon fontSize='small' />
</IconButton>

View File

@ -12,6 +12,10 @@ import { MilestoneTransitionDisplay } from '../ReleasePlanMilestone/MilestoneTra
import { ReleasePlanMilestone } from '../ReleasePlanMilestone/ReleasePlanMilestone.tsx';
import type { MilestoneStatus } from '../ReleasePlanMilestone/ReleasePlanMilestoneStatus.tsx';
import { MilestoneProgressionForm } from '../MilestoneProgressionForm/MilestoneProgressionForm.tsx';
import { useMilestoneProgressionsApi } from 'hooks/api/actions/useMilestoneProgressionsApi/useMilestoneProgressionsApi';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
const StyledConnection = styled('div', {
shouldForwardProp: (prop) => prop !== 'isCompleted',
@ -124,12 +128,71 @@ export const ReleasePlanMilestoneItem = ({
featureName,
onUpdate,
}: IReleasePlanMilestoneItemProps) => {
const { createMilestoneProgression, updateMilestoneProgression } =
useMilestoneProgressionsApi();
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
const { setToastData, setToastApiError } = useToast();
const isNotLastMilestone = index < milestones.length - 1;
const isProgressionFormOpen = progressionFormOpenIndex === index;
const nextMilestoneId = milestones[index + 1]?.id || '';
const handleOpenProgressionForm = () => onSetProgressionFormOpenIndex(index);
const handleCloseProgressionForm = () => onSetProgressionFormOpenIndex(null);
// Unified handler for creating progression
const handleCreateProgression = async (
payload: CreateMilestoneProgressionSchema,
) => {
if (isChangeRequestConfigured(environment)) {
onProgressionChangeRequestSubmit(payload);
handleCloseProgressionForm();
return;
}
try {
await createMilestoneProgression(
projectId,
environment,
featureName,
payload,
);
setToastData({
type: 'success',
text: 'Automation configured successfully',
});
await onProgressionSave();
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
// Unified handler for updating progression
const handleUpdateProgression = async (
payload: UpdateMilestoneProgressionSchema,
) => {
if (isChangeRequestConfigured(environment)) {
onUpdateProgressionChangeRequestSubmit(milestone.id, payload);
return;
}
try {
await updateMilestoneProgression(
projectId,
environment,
featureName,
milestone.id,
payload,
);
setToastData({
type: 'success',
text: 'Automation updated successfully',
});
await onUpdate();
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
const status: MilestoneStatus =
milestone.id === activeMilestoneId
? environmentIsDisabled
@ -168,27 +231,16 @@ export const ReleasePlanMilestoneItem = ({
<MilestoneProgressionForm
sourceMilestoneId={milestone.id}
targetMilestoneId={nextMilestoneId}
projectId={projectId}
environment={environment}
featureName={featureName}
onSave={onProgressionSave}
onSubmit={handleCreateProgression}
onCancel={handleCloseProgressionForm}
onChangeRequestSubmit={(payload) =>
onProgressionChangeRequestSubmit(payload)
}
/>
) : effectiveTransitionCondition ? (
<MilestoneTransitionDisplay
intervalMinutes={effectiveTransitionCondition.intervalMinutes}
onSave={handleUpdateProgression}
onDelete={() => onDeleteProgression(milestone)}
milestoneName={milestone.name}
status={status}
projectId={projectId}
environment={environment}
featureName={featureName}
sourceMilestoneId={milestone.id}
onUpdate={onUpdate}
onChangeRequestSubmit={onUpdateProgressionChangeRequestSubmit}
hasPendingUpdate={hasPendingUpdate}
hasPendingDelete={hasPendingDelete}
/>