mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
refactor: better type safety
This commit is contained in:
parent
e1d21adc9f
commit
a3e4c1095d
@ -15,7 +15,10 @@ import {
|
|||||||
Deleted,
|
Deleted,
|
||||||
} from './Change.styles.tsx';
|
} from './Change.styles.tsx';
|
||||||
import type { ChangeMilestoneProgressionSchema } from 'openapi';
|
import type { ChangeMilestoneProgressionSchema } from 'openapi';
|
||||||
import { MilestoneListRenderer } from './MilestoneListRenderer.tsx';
|
import {
|
||||||
|
ReadonlyMilestoneListRenderer,
|
||||||
|
EditableMilestoneListRenderer,
|
||||||
|
} from './MilestoneListRenderer.tsx';
|
||||||
import { applyProgressionChanges } from './applyProgressionChanges.js';
|
import { applyProgressionChanges } from './applyProgressionChanges.js';
|
||||||
import { EventDiff } from 'component/events/EventDiff/EventDiff';
|
import { EventDiff } from 'component/events/EventDiff/EventDiff';
|
||||||
|
|
||||||
@ -72,11 +75,11 @@ export const ConsolidatedProgressionChanges: FC<{
|
|||||||
feature: IChangeRequestFeature;
|
feature: IChangeRequestFeature;
|
||||||
currentReleasePlan?: IReleasePlan;
|
currentReleasePlan?: IReleasePlan;
|
||||||
changeRequestState: ChangeRequestState;
|
changeRequestState: ChangeRequestState;
|
||||||
onUpdateChangeRequestSubmit?: (
|
onUpdateChangeRequestSubmit: (
|
||||||
sourceMilestoneId: string,
|
sourceMilestoneId: string,
|
||||||
payload: ChangeMilestoneProgressionSchema,
|
payload: ChangeMilestoneProgressionSchema,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
onDeleteChangeRequestSubmit?: (sourceMilestoneId: string) => Promise<void>;
|
onDeleteChangeRequestSubmit: (sourceMilestoneId: string) => Promise<void>;
|
||||||
}> = ({
|
}> = ({
|
||||||
feature,
|
feature,
|
||||||
currentReleasePlan,
|
currentReleasePlan,
|
||||||
@ -113,6 +116,9 @@ export const ConsolidatedProgressionChanges: FC<{
|
|||||||
basePlan,
|
basePlan,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const readonly =
|
||||||
|
changeRequestState === 'Applied' || changeRequestState === 'Cancelled';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTabs>
|
<StyledTabs>
|
||||||
<ChangeItemWrapper>
|
<ChangeItemWrapper>
|
||||||
@ -137,16 +143,25 @@ export const ConsolidatedProgressionChanges: FC<{
|
|||||||
</div>
|
</div>
|
||||||
</ChangeItemWrapper>
|
</ChangeItemWrapper>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<MilestoneListRenderer
|
{readonly ? (
|
||||||
plan={modifiedPlan}
|
<ReadonlyMilestoneListRenderer
|
||||||
changeRequestState={changeRequestState}
|
plan={modifiedPlan}
|
||||||
milestonesWithAutomation={milestonesWithAutomation}
|
milestonesWithAutomation={milestonesWithAutomation}
|
||||||
milestonesWithDeletedAutomation={
|
milestonesWithDeletedAutomation={
|
||||||
milestonesWithDeletedAutomation
|
milestonesWithDeletedAutomation
|
||||||
}
|
}
|
||||||
onUpdateAutomation={onUpdateChangeRequestSubmit}
|
/>
|
||||||
onDeleteAutomation={onDeleteChangeRequestSubmit}
|
) : (
|
||||||
/>
|
<EditableMilestoneListRenderer
|
||||||
|
plan={modifiedPlan}
|
||||||
|
milestonesWithAutomation={milestonesWithAutomation}
|
||||||
|
milestonesWithDeletedAutomation={
|
||||||
|
milestonesWithDeletedAutomation
|
||||||
|
}
|
||||||
|
onUpdateAutomation={onUpdateChangeRequestSubmit}
|
||||||
|
onDeleteAutomation={onDeleteChangeRequestSubmit}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel variant='diff'>
|
<TabPanel variant='diff'>
|
||||||
<EventDiff
|
<EventDiff
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import type { IReleasePlan } from 'interfaces/releasePlans';
|
import type { IReleasePlan } from 'interfaces/releasePlans';
|
||||||
import type { ChangeMilestoneProgressionSchema } from 'openapi';
|
import type { ChangeMilestoneProgressionSchema } from 'openapi';
|
||||||
import type { ChangeRequestState } from 'component/changeRequest/changeRequest.types';
|
|
||||||
import { ReleasePlanMilestone } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone';
|
import { ReleasePlanMilestone } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone';
|
||||||
import { MilestoneAutomationSection } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneAutomationSection.tsx';
|
import { MilestoneAutomationSection } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneAutomationSection.tsx';
|
||||||
import { MilestoneTransitionDisplay } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneTransitionDisplay.tsx';
|
import { MilestoneTransitionDisplay } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/MilestoneTransitionDisplay.tsx';
|
||||||
@ -15,29 +14,26 @@ const StyledConnection = styled('div')(({ theme }) => ({
|
|||||||
marginLeft: theme.spacing(3.25),
|
marginLeft: theme.spacing(3.25),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface MilestoneListRendererProps {
|
interface MilestoneListRendererCoreProps {
|
||||||
plan: IReleasePlan;
|
plan: IReleasePlan;
|
||||||
changeRequestState: ChangeRequestState;
|
readonly: boolean;
|
||||||
milestonesWithAutomation?: Set<string>;
|
milestonesWithAutomation: Set<string>;
|
||||||
milestonesWithDeletedAutomation?: Set<string>;
|
milestonesWithDeletedAutomation: Set<string>;
|
||||||
onUpdateAutomation?: (
|
onUpdateAutomation: (
|
||||||
sourceMilestoneId: string,
|
sourceMilestoneId: string,
|
||||||
payload: ChangeMilestoneProgressionSchema,
|
payload: ChangeMilestoneProgressionSchema,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
onDeleteAutomation?: (sourceMilestoneId: string) => void;
|
onDeleteAutomation: (sourceMilestoneId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MilestoneListRenderer = ({
|
const MilestoneListRendererCore = ({
|
||||||
plan,
|
plan,
|
||||||
changeRequestState,
|
readonly,
|
||||||
milestonesWithAutomation = new Set(),
|
milestonesWithAutomation,
|
||||||
milestonesWithDeletedAutomation = new Set(),
|
milestonesWithDeletedAutomation,
|
||||||
onUpdateAutomation,
|
onUpdateAutomation,
|
||||||
onDeleteAutomation,
|
onDeleteAutomation,
|
||||||
}: MilestoneListRendererProps) => {
|
}: MilestoneListRendererCoreProps) => {
|
||||||
// TODO: Split into read and write model at the type level to avoid having optional handlers
|
|
||||||
const readonly =
|
|
||||||
changeRequestState === 'Applied' || changeRequestState === 'Cancelled';
|
|
||||||
const status: MilestoneStatus = 'not-started';
|
const status: MilestoneStatus = 'not-started';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -70,14 +66,14 @@ export const MilestoneListRenderer = ({
|
|||||||
}
|
}
|
||||||
targetMilestoneId={nextMilestoneId}
|
targetMilestoneId={nextMilestoneId}
|
||||||
onSave={async (payload) => {
|
onSave={async (payload) => {
|
||||||
await onUpdateAutomation?.(
|
await onUpdateAutomation(
|
||||||
milestone.id,
|
milestone.id,
|
||||||
payload,
|
payload,
|
||||||
);
|
);
|
||||||
return { shouldReset: true };
|
return { shouldReset: true };
|
||||||
}}
|
}}
|
||||||
onDelete={() =>
|
onDelete={() =>
|
||||||
onDeleteAutomation?.(milestone.id)
|
onDeleteAutomation(milestone.id)
|
||||||
}
|
}
|
||||||
milestoneName={milestone.name}
|
milestoneName={milestone.name}
|
||||||
status={status}
|
status={status}
|
||||||
@ -102,3 +98,56 @@ export const MilestoneListRenderer = ({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface ReadonlyMilestoneListRendererProps {
|
||||||
|
plan: IReleasePlan;
|
||||||
|
milestonesWithAutomation?: Set<string>;
|
||||||
|
milestonesWithDeletedAutomation?: Set<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ReadonlyMilestoneListRenderer = ({
|
||||||
|
plan,
|
||||||
|
milestonesWithAutomation = new Set(),
|
||||||
|
milestonesWithDeletedAutomation = new Set(),
|
||||||
|
}: ReadonlyMilestoneListRendererProps) => {
|
||||||
|
return (
|
||||||
|
<MilestoneListRendererCore
|
||||||
|
plan={plan}
|
||||||
|
readonly={true}
|
||||||
|
milestonesWithAutomation={milestonesWithAutomation}
|
||||||
|
milestonesWithDeletedAutomation={milestonesWithDeletedAutomation}
|
||||||
|
onUpdateAutomation={async () => {}}
|
||||||
|
onDeleteAutomation={() => {}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface EditableMilestoneListRendererProps {
|
||||||
|
plan: IReleasePlan;
|
||||||
|
milestonesWithAutomation?: Set<string>;
|
||||||
|
milestonesWithDeletedAutomation?: Set<string>;
|
||||||
|
onUpdateAutomation: (
|
||||||
|
sourceMilestoneId: string,
|
||||||
|
payload: ChangeMilestoneProgressionSchema,
|
||||||
|
) => Promise<void>;
|
||||||
|
onDeleteAutomation: (sourceMilestoneId: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EditableMilestoneListRenderer = ({
|
||||||
|
plan,
|
||||||
|
milestonesWithAutomation = new Set(),
|
||||||
|
milestonesWithDeletedAutomation = new Set(),
|
||||||
|
onUpdateAutomation,
|
||||||
|
onDeleteAutomation,
|
||||||
|
}: EditableMilestoneListRendererProps) => {
|
||||||
|
return (
|
||||||
|
<MilestoneListRendererCore
|
||||||
|
plan={plan}
|
||||||
|
readonly={false}
|
||||||
|
milestonesWithAutomation={milestonesWithAutomation}
|
||||||
|
milestonesWithDeletedAutomation={milestonesWithDeletedAutomation}
|
||||||
|
onUpdateAutomation={onUpdateAutomation}
|
||||||
|
onDeleteAutomation={onDeleteAutomation}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@ -10,7 +10,10 @@ import { EventDiff } from 'component/events/EventDiff/EventDiff';
|
|||||||
import { Tab, TabList, TabPanel, Tabs } from './ChangeTabComponents.tsx';
|
import { Tab, TabList, TabPanel, Tabs } from './ChangeTabComponents.tsx';
|
||||||
import { Action, ChangeItemInfo, ChangeItemWrapper } from './Change.styles.tsx';
|
import { Action, ChangeItemInfo, ChangeItemWrapper } from './Change.styles.tsx';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import { MilestoneListRenderer } from './MilestoneListRenderer.tsx';
|
import {
|
||||||
|
ReadonlyMilestoneListRenderer,
|
||||||
|
EditableMilestoneListRenderer,
|
||||||
|
} from './MilestoneListRenderer.tsx';
|
||||||
import { applyProgressionChanges } from './applyProgressionChanges.ts';
|
import { applyProgressionChanges } from './applyProgressionChanges.ts';
|
||||||
|
|
||||||
const StyledTabs = styled(Tabs)(({ theme }) => ({
|
const StyledTabs = styled(Tabs)(({ theme }) => ({
|
||||||
@ -24,11 +27,11 @@ interface ProgressionChangeProps {
|
|||||||
currentReleasePlan?: IReleasePlan;
|
currentReleasePlan?: IReleasePlan;
|
||||||
actions?: ReactNode;
|
actions?: ReactNode;
|
||||||
changeRequestState: ChangeRequestState;
|
changeRequestState: ChangeRequestState;
|
||||||
onUpdateChangeRequestSubmit?: (
|
onUpdateChangeRequestSubmit: (
|
||||||
sourceMilestoneId: string,
|
sourceMilestoneId: string,
|
||||||
payload: ChangeMilestoneProgressionSchema,
|
payload: ChangeMilestoneProgressionSchema,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
onDeleteChangeRequestSubmit?: (sourceMilestoneId: string) => void;
|
onDeleteChangeRequestSubmit: (sourceMilestoneId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProgressionChange: FC<ProgressionChangeProps> = ({
|
export const ProgressionChange: FC<ProgressionChangeProps> = ({
|
||||||
@ -62,6 +65,9 @@ export const ProgressionChange: FC<ProgressionChangeProps> = ({
|
|||||||
(milestone) => milestone.id === sourceId,
|
(milestone) => milestone.id === sourceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const readonly =
|
||||||
|
changeRequestState === 'Applied' || changeRequestState === 'Cancelled';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTabs>
|
<StyledTabs>
|
||||||
<ChangeItemWrapper>
|
<ChangeItemWrapper>
|
||||||
@ -80,15 +86,23 @@ export const ProgressionChange: FC<ProgressionChangeProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</ChangeItemWrapper>
|
</ChangeItemWrapper>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<MilestoneListRenderer
|
{readonly ? (
|
||||||
plan={modifiedPlan}
|
<ReadonlyMilestoneListRenderer
|
||||||
changeRequestState={changeRequestState}
|
plan={modifiedPlan}
|
||||||
milestonesWithAutomation={
|
milestonesWithAutomation={
|
||||||
new Set([sourceId].filter(Boolean))
|
new Set([sourceId].filter(Boolean))
|
||||||
}
|
}
|
||||||
onUpdateAutomation={onUpdateChangeRequestSubmit}
|
/>
|
||||||
onDeleteAutomation={onDeleteChangeRequestSubmit}
|
) : (
|
||||||
/>
|
<EditableMilestoneListRenderer
|
||||||
|
plan={modifiedPlan}
|
||||||
|
milestonesWithAutomation={
|
||||||
|
new Set([sourceId].filter(Boolean))
|
||||||
|
}
|
||||||
|
onUpdateAutomation={onUpdateChangeRequestSubmit}
|
||||||
|
onDeleteAutomation={onDeleteChangeRequestSubmit}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel variant='diff'>
|
<TabPanel variant='diff'>
|
||||||
<EventDiff
|
<EventDiff
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
|||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { calculateMilestoneStatus } from './milestoneStatusUtils.js';
|
import { calculateMilestoneStatus } from './milestoneStatusUtils.js';
|
||||||
import { usePendingProgressionChanges } from './usePendingProgressionChanges.js';
|
import { getPendingProgressionData } from './pendingProgressionChanges.js';
|
||||||
import { MilestoneAutomation } from './MilestoneAutomation.tsx';
|
import { MilestoneAutomation } from './MilestoneAutomation.tsx';
|
||||||
|
|
||||||
const StyledConnection = styled('div', {
|
const StyledConnection = styled('div', {
|
||||||
@ -130,7 +130,7 @@ export const ReleasePlanMilestoneItem = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { pendingProgressionChange, effectiveTransitionCondition } =
|
const { pendingProgressionChange, effectiveTransitionCondition } =
|
||||||
usePendingProgressionChanges(milestone, getPendingProgressionChange);
|
getPendingProgressionData(milestone, getPendingProgressionChange);
|
||||||
|
|
||||||
const shouldShowAutomation =
|
const shouldShowAutomation =
|
||||||
isNotLastMilestone && milestoneProgressionsEnabled;
|
isNotLastMilestone && milestoneProgressionsEnabled;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ interface PendingProgressionChangeResult {
|
|||||||
effectiveTransitionCondition: IReleasePlanMilestone['transitionCondition'];
|
effectiveTransitionCondition: IReleasePlanMilestone['transitionCondition'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePendingProgressionChanges = (
|
export const getPendingProgressionData = (
|
||||||
milestone: IReleasePlanMilestone,
|
milestone: IReleasePlanMilestone,
|
||||||
getPendingProgressionChange: IReleasePlanMilestoneItemProps['getPendingProgressionChange'],
|
getPendingProgressionChange: IReleasePlanMilestoneItemProps['getPendingProgressionChange'],
|
||||||
): PendingProgressionChangeResult => {
|
): PendingProgressionChangeResult => {
|
||||||
Loading…
Reference in New Issue
Block a user