From 5869c7e04ff98be4c915d797a9476dec87e5cd7a Mon Sep 17 00:00:00 2001 From: FredrikOseberg Date: Tue, 21 Oct 2025 14:07:19 +0200 Subject: [PATCH] refactor: simplify components --- .../Changes/Change/ReleasePlanChange.tsx | 27 +++---- .../MilestoneProgressionForm.tsx | 63 +-------------- .../MilestoneTransitionDisplay.tsx | 67 +++------------- .../ReleasePlanMilestoneItem.tsx | 78 +++++++++++++++---- 4 files changed, 87 insertions(+), 148 deletions(-) diff --git a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ReleasePlanChange.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ReleasePlanChange.tsx index c3af2824ac..a853949795 100644 --- a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ReleasePlanChange.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ReleasePlanChange.tsx @@ -340,15 +340,12 @@ const CreateMilestoneProgression: FC<{ { + 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<{ { + 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<{ { + 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)} /> diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/MilestoneProgressionForm/MilestoneProgressionForm.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/MilestoneProgressionForm/MilestoneProgressionForm.tsx index 2ae484db9b..676b73eb33 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/MilestoneProgressionForm/MilestoneProgressionForm.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/MilestoneProgressionForm/MilestoneProgressionForm.tsx @@ -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; 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} /> @@ -161,7 +108,6 @@ export const MilestoneProgressionForm = ({ variant='outlined' onClick={onCancel} size='small' - disabled={isSubmitting} > Cancel @@ -170,9 +116,8 @@ export const MilestoneProgressionForm = ({ color='primary' onClick={handleSubmit} size='small' - disabled={isSubmitting} > - {isSubmitting ? 'Saving...' : 'Save'} + Save diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneTransitionDisplay.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneTransitionDisplay.tsx index 8a98deeb01..9232c440fc 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneTransitionDisplay.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneTransitionDisplay.tsx @@ -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; 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} /> @@ -187,9 +140,8 @@ export const MilestoneTransitionDisplay = ({ color='primary' onClick={handleSave} size='small' - disabled={isSubmitting} > - {isSubmitting ? 'Saving...' : 'Save'} + Save )} {showDraftBadge && ( @@ -202,7 +154,6 @@ export const MilestoneTransitionDisplay = ({ size='small' aria-label={`Delete automation for ${milestoneName}`} sx={{ padding: 0.5 }} - disabled={isSubmitting} > diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestoneItem/ReleasePlanMilestoneItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestoneItem/ReleasePlanMilestoneItem.tsx index 0441e612ed..bb6fb93db3 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestoneItem/ReleasePlanMilestoneItem.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestoneItem/ReleasePlanMilestoneItem.tsx @@ -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 = ({ - onProgressionChangeRequestSubmit(payload) - } /> ) : effectiveTransitionCondition ? ( onDeleteProgression(milestone)} milestoneName={milestone.name} status={status} - projectId={projectId} - environment={environment} - featureName={featureName} - sourceMilestoneId={milestone.id} - onUpdate={onUpdate} - onChangeRequestSubmit={onUpdateProgressionChangeRequestSubmit} hasPendingUpdate={hasPendingUpdate} hasPendingDelete={hasPendingDelete} />