mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-26 01:17:00 +02:00
refactor: strategy draggable item is now proj/env agnostic (#9411)
Updates `StrategyDraggableItem` (and `StrategyItem`) to be project/env agnostic. They now instead expect you to pass in the required header items (CR badges, strategy actions) at the call site. Updates their usage in the feature env accordion, and the release plan card. All components that have been updated are part of the new overview rework. The legacy components (which are used when the flag is off) remain untouched. Also makes a few small tweaks explained in inline comments. ## Rendered Milestone card (with flag on):  Milestone card (with flag off):  Feature env accordion (flag on (no change)):  Feature env accordion (flag off): 
This commit is contained in:
parent
36fd26baa4
commit
2e086161eb
@ -0,0 +1,28 @@
|
||||
import { Badge } from 'component/common/Badge/Badge';
|
||||
import type { IFeatureChange } from '../changeRequest.types';
|
||||
import type { SxProps } from '@mui/material';
|
||||
|
||||
export const ChangeRequestDraftStatusBadge = ({
|
||||
changeAction,
|
||||
sx,
|
||||
}: {
|
||||
changeAction: IFeatureChange['action'];
|
||||
sx?: SxProps;
|
||||
}) => {
|
||||
switch (changeAction) {
|
||||
case 'updateStrategy':
|
||||
return (
|
||||
<Badge color='warning' sx={sx}>
|
||||
Modified in draft
|
||||
</Badge>
|
||||
);
|
||||
case 'deleteStrategy':
|
||||
return (
|
||||
<Badge color='error' sx={sx}>
|
||||
Deleted in draft
|
||||
</Badge>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
@ -3,27 +3,23 @@ import type { DragEventHandler, FC, ReactNode } from 'react';
|
||||
import DragIndicator from '@mui/icons-material/DragIndicator';
|
||||
import { Box, IconButton, styled } from '@mui/material';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import {
|
||||
formatStrategyName,
|
||||
getFeatureStrategyIcon,
|
||||
} from 'utils/strategyNames';
|
||||
import { formatStrategyName } from 'utils/strategyNames';
|
||||
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import type { PlaygroundStrategySchema } from 'openapi';
|
||||
import { Badge } from '../Badge/Badge';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
interface IStrategyItemContainerProps {
|
||||
type StrategyItemContainerProps = {
|
||||
strategy: IFeatureStrategy | PlaygroundStrategySchema;
|
||||
onDragStart?: DragEventHandler<HTMLButtonElement>;
|
||||
onDragEnd?: DragEventHandler<HTMLButtonElement>;
|
||||
actions?: ReactNode;
|
||||
orderNumber?: number;
|
||||
headerItemsRight?: ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
description?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
};
|
||||
|
||||
const DragIcon = styled(IconButton)({
|
||||
padding: 0,
|
||||
@ -76,17 +72,15 @@ const NewStyledHeader = styled('div', {
|
||||
}),
|
||||
);
|
||||
|
||||
export const StrategyItemContainer: FC<IStrategyItemContainerProps> = ({
|
||||
export const StrategyItemContainer: FC<StrategyItemContainerProps> = ({
|
||||
strategy,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
actions,
|
||||
headerItemsRight,
|
||||
children,
|
||||
style = {},
|
||||
description,
|
||||
}) => {
|
||||
const Icon = getFeatureStrategyIcon(strategy.name);
|
||||
|
||||
const StrategyHeaderLink: React.FC<{ children?: React.ReactNode }> =
|
||||
'links' in strategy
|
||||
? ({ children }) => <Link to={strategy.links.edit}>{children}</Link>
|
||||
@ -118,11 +112,6 @@ export const StrategyItemContainer: FC<IStrategyItemContainerProps> = ({
|
||||
</DragIcon>
|
||||
)}
|
||||
/>
|
||||
<Icon
|
||||
sx={{
|
||||
fill: (theme) => theme.palette.action.disabled,
|
||||
}}
|
||||
/>
|
||||
<StyledHeaderContainer>
|
||||
<StrategyHeaderLink>
|
||||
<StringTruncator
|
||||
@ -167,7 +156,7 @@ export const StrategyItemContainer: FC<IStrategyItemContainerProps> = ({
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{actions}
|
||||
{headerItemsRight}
|
||||
</Box>
|
||||
</NewStyledHeader>
|
||||
<Box sx={{ p: 2, pt: 0 }}>{children}</Box>
|
||||
|
@ -20,9 +20,9 @@ import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans';
|
||||
import { StrategyDraggableItem } from './StrategyDraggableItem/StrategyDraggableItem';
|
||||
import { ReleasePlan } from '../../../ReleasePlan/ReleasePlan';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
||||
import { ProjectEnvironmentStrategyDraggableItem } from './StrategyDraggableItem/ProjectEnvironmentStrategyDraggableItem';
|
||||
|
||||
interface IEnvironmentAccordionBodyProps {
|
||||
isDisabled: boolean;
|
||||
@ -252,7 +252,7 @@ export const EnvironmentAccordionBody = ({
|
||||
<StrategySeparator />
|
||||
) : null}
|
||||
|
||||
<StrategyDraggableItem
|
||||
<ProjectEnvironmentStrategyDraggableItem
|
||||
strategy={strategy}
|
||||
index={index}
|
||||
environmentName={
|
||||
@ -287,7 +287,7 @@ export const EnvironmentAccordionBody = ({
|
||||
<StrategySeparator />
|
||||
) : null}
|
||||
|
||||
<StrategyDraggableItem
|
||||
<ProjectEnvironmentStrategyDraggableItem
|
||||
strategy={strategy}
|
||||
index={
|
||||
index + pageIndex * pageSize
|
||||
|
@ -0,0 +1,137 @@
|
||||
import { type DragEventHandler, type RefObject, useRef } from 'react';
|
||||
import { Box, useMediaQuery, useTheme } from '@mui/material';
|
||||
import type { IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useStrategyChangesFromRequest } from './StrategyItem/useStrategyChangesFromRequest';
|
||||
import { ChangesScheduledBadge } from 'component/changeRequest/ModifiedInChangeRequestStatusBadge/ChangesScheduledBadge';
|
||||
import { useScheduledChangeRequestsWithStrategy } from 'hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy';
|
||||
import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
|
||||
import { ChangeRequestDraftStatusBadge } from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestDraftStatusBadge';
|
||||
import { CopyStrategyIconMenu } from './StrategyItem/CopyStrategyIconMenu/CopyStrategyIconMenu';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import Edit from '@mui/icons-material/Edit';
|
||||
import MenuStrategyRemove from './StrategyItem/MenuStrategyRemove/MenuStrategyRemove';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { UPDATE_FEATURE_STRATEGY } from '@server/types/permissions';
|
||||
import { StrategyDraggableItem } from './StrategyDraggableItem';
|
||||
|
||||
type ProjectEnvironmentStrategyDraggableItemProps = {
|
||||
strategy: IFeatureStrategy;
|
||||
environmentName: string;
|
||||
index: number;
|
||||
otherEnvironments?: IFeatureEnvironment['name'][];
|
||||
isDragging?: boolean;
|
||||
onDragStartRef?: (
|
||||
ref: RefObject<HTMLDivElement>,
|
||||
index: number,
|
||||
) => DragEventHandler<HTMLButtonElement>;
|
||||
onDragOver?: (
|
||||
ref: RefObject<HTMLDivElement>,
|
||||
index: number,
|
||||
) => DragEventHandler<HTMLDivElement>;
|
||||
onDragEnd?: () => void;
|
||||
};
|
||||
|
||||
const onDragNoOp = () => () => {};
|
||||
|
||||
export const ProjectEnvironmentStrategyDraggableItem = ({
|
||||
strategy,
|
||||
index,
|
||||
environmentName,
|
||||
otherEnvironments,
|
||||
isDragging,
|
||||
onDragStartRef = onDragNoOp,
|
||||
onDragOver = onDragNoOp,
|
||||
onDragEnd = onDragNoOp,
|
||||
}: ProjectEnvironmentStrategyDraggableItemProps) => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const strategyChangesFromRequest = useStrategyChangesFromRequest(
|
||||
projectId,
|
||||
featureId,
|
||||
environmentName,
|
||||
strategy.id,
|
||||
);
|
||||
|
||||
const { changeRequests: scheduledChanges } =
|
||||
useScheduledChangeRequestsWithStrategy(projectId, strategy.id);
|
||||
|
||||
const editStrategyPath = formatEditStrategyPath(
|
||||
projectId,
|
||||
featureId,
|
||||
environmentName,
|
||||
strategy.id,
|
||||
);
|
||||
|
||||
const draftChange = strategyChangesFromRequest?.find(
|
||||
({ isScheduledChange }) => !isScheduledChange,
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={strategy.id}
|
||||
ref={ref}
|
||||
onDragOver={onDragOver(ref, index)}
|
||||
sx={{ opacity: isDragging ? '0.5' : '1' }}
|
||||
>
|
||||
<StrategyDraggableItem
|
||||
strategy={strategy}
|
||||
onDragEnd={onDragEnd}
|
||||
onDragStartRef={onDragStartRef}
|
||||
onDragOver={onDragOver}
|
||||
index={index}
|
||||
headerItemsRight={
|
||||
<>
|
||||
{draftChange && !isSmallScreen ? (
|
||||
<ChangeRequestDraftStatusBadge
|
||||
sx={{ mr: 1.5 }}
|
||||
changeAction={draftChange.change.action}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{scheduledChanges &&
|
||||
scheduledChanges.length > 0 &&
|
||||
!isSmallScreen ? (
|
||||
<ChangesScheduledBadge
|
||||
scheduledChangeRequestIds={(
|
||||
scheduledChanges ?? []
|
||||
).map((scheduledChange) => scheduledChange.id)}
|
||||
/>
|
||||
) : null}
|
||||
{otherEnvironments && otherEnvironments?.length > 0 ? (
|
||||
<CopyStrategyIconMenu
|
||||
environmentId={environmentName}
|
||||
environments={otherEnvironments as string[]}
|
||||
strategy={strategy}
|
||||
/>
|
||||
) : null}
|
||||
<PermissionIconButton
|
||||
permission={UPDATE_FEATURE_STRATEGY}
|
||||
environmentId={environmentName}
|
||||
projectId={projectId}
|
||||
component={Link}
|
||||
to={editStrategyPath}
|
||||
tooltipProps={{
|
||||
title: 'Edit strategy',
|
||||
}}
|
||||
data-testid={`STRATEGY_EDIT-${strategy.name}`}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
<MenuStrategyRemove
|
||||
projectId={projectId}
|
||||
featureId={featureId}
|
||||
environmentId={environmentName}
|
||||
strategy={strategy}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,6 +1,5 @@
|
||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { StrategyDraggableItem } from './StrategyDraggableItem';
|
||||
import { vi } from 'vitest';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import { screen } from '@testing-library/react';
|
||||
@ -9,6 +8,7 @@ import type {
|
||||
ChangeRequestType,
|
||||
ChangeRequestAction,
|
||||
} from 'component/changeRequest/changeRequest.types';
|
||||
import { ProjectEnvironmentStrategyDraggableItem } from './ProjectEnvironmentStrategyDraggableItem';
|
||||
|
||||
const server = testServerSetup();
|
||||
|
||||
@ -211,7 +211,7 @@ const Component = () => {
|
||||
<Route
|
||||
path={'/projects/:projectId/features/:featureId'}
|
||||
element={
|
||||
<StrategyDraggableItem
|
||||
<ProjectEnvironmentStrategyDraggableItem
|
||||
strategy={strategy}
|
||||
environmentName={'production'}
|
||||
index={1}
|
||||
|
@ -1,27 +1,19 @@
|
||||
import { type DragEventHandler, type RefObject, useRef } from 'react';
|
||||
import { Box, useMediaQuery, useTheme } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import type { IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import {
|
||||
type DragEventHandler,
|
||||
type RefObject,
|
||||
useRef,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import {
|
||||
useStrategyChangesFromRequest,
|
||||
type UseStrategyChangeFromRequestResult,
|
||||
} from './StrategyItem/useStrategyChangesFromRequest';
|
||||
import { ChangesScheduledBadge } from 'component/changeRequest/ModifiedInChangeRequestStatusBadge/ChangesScheduledBadge';
|
||||
import type { IFeatureChange } from 'component/changeRequest/changeRequest.types';
|
||||
import { Badge } from 'component/common/Badge/Badge';
|
||||
import {
|
||||
type ScheduledChangeRequestViewModel,
|
||||
useScheduledChangeRequestsWithStrategy,
|
||||
} from 'hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy';
|
||||
import { StrategyItem } from './StrategyItem/StrategyItem';
|
||||
|
||||
interface IStrategyDraggableItemProps {
|
||||
const onDragNoOp = () => () => {};
|
||||
|
||||
type StrategyDraggableItemProps = {
|
||||
headerItemsRight: ReactNode;
|
||||
strategy: IFeatureStrategy;
|
||||
environmentName: string;
|
||||
index: number;
|
||||
otherEnvironments?: IFeatureEnvironment['name'][];
|
||||
isDragging?: boolean;
|
||||
onDragStartRef?: (
|
||||
ref: RefObject<HTMLDivElement>,
|
||||
@ -32,32 +24,18 @@ interface IStrategyDraggableItemProps {
|
||||
index: number,
|
||||
) => DragEventHandler<HTMLDivElement>;
|
||||
onDragEnd?: () => void;
|
||||
}
|
||||
|
||||
const onDragNoOp = () => () => {};
|
||||
};
|
||||
|
||||
export const StrategyDraggableItem = ({
|
||||
strategy,
|
||||
index,
|
||||
environmentName,
|
||||
otherEnvironments,
|
||||
isDragging,
|
||||
onDragStartRef = onDragNoOp,
|
||||
onDragOver = onDragNoOp,
|
||||
onDragEnd = onDragNoOp,
|
||||
}: IStrategyDraggableItemProps) => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
headerItemsRight,
|
||||
}: StrategyDraggableItemProps) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const strategyChangesFromRequest = useStrategyChangesFromRequest(
|
||||
projectId,
|
||||
featureId,
|
||||
environmentName,
|
||||
strategy.id,
|
||||
);
|
||||
|
||||
const { changeRequests: scheduledChangesUsingStrategy } =
|
||||
useScheduledChangeRequestsWithStrategy(projectId, strategy.id);
|
||||
|
||||
return (
|
||||
<Box
|
||||
@ -67,79 +45,11 @@ export const StrategyDraggableItem = ({
|
||||
sx={{ opacity: isDragging ? '0.5' : '1' }}
|
||||
>
|
||||
<StrategyItem
|
||||
headerItemsRight={headerItemsRight}
|
||||
strategy={strategy}
|
||||
environmentId={environmentName}
|
||||
otherEnvironments={otherEnvironments}
|
||||
onDragStart={onDragStartRef(ref, index)}
|
||||
onDragEnd={onDragEnd}
|
||||
orderNumber={index + 1}
|
||||
headerChildren={renderHeaderChildren(
|
||||
strategyChangesFromRequest,
|
||||
scheduledChangesUsingStrategy,
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const ChangeRequestStatusBadge = ({
|
||||
change,
|
||||
}: {
|
||||
change: IFeatureChange | undefined;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
if (isSmallScreen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ mr: 1.5 }}>
|
||||
<ConditionallyRender
|
||||
condition={change?.action === 'updateStrategy'}
|
||||
show={<Badge color='warning'>Modified in draft</Badge>}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={change?.action === 'deleteStrategy'}
|
||||
show={<Badge color='error'>Deleted in draft</Badge>}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const renderHeaderChildren = (
|
||||
changes?: UseStrategyChangeFromRequestResult,
|
||||
scheduledChanges?: ScheduledChangeRequestViewModel[],
|
||||
): JSX.Element[] => {
|
||||
const badges: JSX.Element[] = [];
|
||||
if (changes?.length === 0 && scheduledChanges?.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const draftChange = changes?.find(
|
||||
({ isScheduledChange }) => !isScheduledChange,
|
||||
);
|
||||
|
||||
if (draftChange) {
|
||||
badges.push(
|
||||
<ChangeRequestStatusBadge
|
||||
key={`draft-change#${draftChange.change.id}`}
|
||||
change={draftChange.change}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
if (scheduledChanges && scheduledChanges.length > 0) {
|
||||
badges.push(
|
||||
<ChangesScheduledBadge
|
||||
key='scheduled-changes'
|
||||
scheduledChangeRequestIds={scheduledChanges.map(
|
||||
(scheduledChange) => scheduledChange.id,
|
||||
)}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
return badges;
|
||||
};
|
||||
|
@ -15,7 +15,6 @@ import { StrategyItemContainer } from 'component/common/StrategyItemContainer/Le
|
||||
import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove';
|
||||
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { Box } from '@mui/material';
|
||||
import { StrategyItemContainer as NewStrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
||||
interface IStrategyItemProps {
|
||||
environmentId: string;
|
||||
strategy: IFeatureStrategy;
|
||||
@ -102,80 +101,3 @@ export const StrategyItem: FC<IStrategyItemProps> = ({
|
||||
</StrategyItemContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const NewStrategyItem: FC<IStrategyItemProps> = ({
|
||||
environmentId,
|
||||
strategy,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
otherEnvironments,
|
||||
orderNumber,
|
||||
headerChildren,
|
||||
}) => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
|
||||
const editStrategyPath = formatEditStrategyPath(
|
||||
projectId,
|
||||
featureId,
|
||||
environmentId,
|
||||
strategy.id,
|
||||
);
|
||||
|
||||
return (
|
||||
<NewStrategyItemContainer
|
||||
strategy={strategy}
|
||||
onDragStart={onDragStart}
|
||||
onDragEnd={onDragEnd}
|
||||
orderNumber={orderNumber}
|
||||
actions={
|
||||
<>
|
||||
{headerChildren}
|
||||
<ConditionallyRender
|
||||
condition={Boolean(
|
||||
otherEnvironments && otherEnvironments?.length > 0,
|
||||
)}
|
||||
show={() => (
|
||||
<CopyStrategyIconMenu
|
||||
environmentId={environmentId}
|
||||
environments={otherEnvironments as string[]}
|
||||
strategy={strategy}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<PermissionIconButton
|
||||
permission={UPDATE_FEATURE_STRATEGY}
|
||||
environmentId={environmentId}
|
||||
projectId={projectId}
|
||||
component={Link}
|
||||
to={editStrategyPath}
|
||||
tooltipProps={{
|
||||
title: 'Edit strategy',
|
||||
}}
|
||||
data-testid={`STRATEGY_EDIT-${strategy.name}`}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
<MenuStrategyRemove
|
||||
projectId={projectId}
|
||||
featureId={featureId}
|
||||
environmentId={environmentId}
|
||||
strategy={strategy}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<StrategyExecution strategy={strategy} />
|
||||
|
||||
{strategy.variants &&
|
||||
strategy.variants.length > 0 &&
|
||||
(strategy.disabled ? (
|
||||
<Box sx={{ opacity: '0.5' }}>
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
</Box>
|
||||
) : (
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
))}
|
||||
</NewStrategyItemContainer>
|
||||
);
|
||||
};
|
||||
|
@ -1,90 +1,29 @@
|
||||
import type { DragEventHandler, FC } from 'react';
|
||||
import Edit from '@mui/icons-material/Edit';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import type { DragEventHandler, FC, ReactNode } from 'react';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
||||
import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { StrategyExecution } from './StrategyExecution/StrategyExecution';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMenu';
|
||||
import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove';
|
||||
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { Box } from '@mui/material';
|
||||
import { StrategyItemContainer as NewStrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
||||
interface IStrategyItemProps {
|
||||
environmentId: string;
|
||||
|
||||
type StrategyItemProps = {
|
||||
headerItemsRight: ReactNode;
|
||||
strategy: IFeatureStrategy;
|
||||
onDragStart?: DragEventHandler<HTMLButtonElement>;
|
||||
onDragEnd?: DragEventHandler<HTMLButtonElement>;
|
||||
otherEnvironments?: IFeatureEnvironment['name'][];
|
||||
orderNumber?: number;
|
||||
headerChildren?: JSX.Element[] | JSX.Element;
|
||||
}
|
||||
};
|
||||
|
||||
export const StrategyItem: FC<IStrategyItemProps> = ({
|
||||
environmentId,
|
||||
export const StrategyItem: FC<StrategyItemProps> = ({
|
||||
strategy,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
otherEnvironments,
|
||||
orderNumber,
|
||||
headerChildren,
|
||||
headerItemsRight,
|
||||
}) => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
|
||||
const editStrategyPath = formatEditStrategyPath(
|
||||
projectId,
|
||||
featureId,
|
||||
environmentId,
|
||||
strategy.id,
|
||||
);
|
||||
|
||||
return (
|
||||
<NewStrategyItemContainer
|
||||
strategy={strategy}
|
||||
onDragStart={onDragStart}
|
||||
onDragEnd={onDragEnd}
|
||||
orderNumber={orderNumber}
|
||||
actions={
|
||||
<>
|
||||
{headerChildren}
|
||||
<ConditionallyRender
|
||||
condition={Boolean(
|
||||
otherEnvironments && otherEnvironments?.length > 0,
|
||||
)}
|
||||
show={() => (
|
||||
<CopyStrategyIconMenu
|
||||
environmentId={environmentId}
|
||||
environments={otherEnvironments as string[]}
|
||||
strategy={strategy}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<PermissionIconButton
|
||||
permission={UPDATE_FEATURE_STRATEGY}
|
||||
environmentId={environmentId}
|
||||
projectId={projectId}
|
||||
component={Link}
|
||||
to={editStrategyPath}
|
||||
tooltipProps={{
|
||||
title: 'Edit strategy',
|
||||
}}
|
||||
data-testid={`STRATEGY_EDIT-${strategy.name}`}
|
||||
>
|
||||
<Edit />
|
||||
</PermissionIconButton>
|
||||
<MenuStrategyRemove
|
||||
projectId={projectId}
|
||||
featureId={featureId}
|
||||
environmentId={environmentId}
|
||||
strategy={strategy}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
headerItemsRight={headerItemsRight}
|
||||
>
|
||||
<StrategyExecution strategy={strategy} />
|
||||
|
||||
|
@ -8,7 +8,6 @@ import { Alert, Pagination, styled } from '@mui/material';
|
||||
import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { StrategyDraggableItem } from '../FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem';
|
||||
import type { IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
@ -24,6 +23,7 @@ import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePla
|
||||
import { ReleasePlan } from '../ReleasePlan/ReleasePlan';
|
||||
import { SectionSeparator } from '../FeatureOverviewEnvironments/FeatureOverviewEnvironment/SectionSeparator/SectionSeparator';
|
||||
import { Badge } from 'component/common/Badge/Badge';
|
||||
import { ProjectEnvironmentStrategyDraggableItem } from '../FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/ProjectEnvironmentStrategyDraggableItem';
|
||||
|
||||
interface IEnvironmentAccordionBodyProps {
|
||||
isDisabled: boolean;
|
||||
@ -256,7 +256,7 @@ export const FeatureOverviewEnvironmentBody = ({
|
||||
!manyStrategiesPagination ? (
|
||||
<>
|
||||
{strategiesToDisplay.map((strategy, index) => (
|
||||
<StrategyDraggableItem
|
||||
<ProjectEnvironmentStrategyDraggableItem
|
||||
key={strategy.id}
|
||||
strategy={strategy}
|
||||
index={index}
|
||||
@ -283,7 +283,7 @@ export const FeatureOverviewEnvironmentBody = ({
|
||||
</Alert>
|
||||
<br />
|
||||
{page.map((strategy, index) => (
|
||||
<StrategyDraggableItem
|
||||
<ProjectEnvironmentStrategyDraggableItem
|
||||
key={strategy.id}
|
||||
strategy={strategy}
|
||||
index={index + pageIndex * pageSize}
|
||||
|
@ -5,10 +5,10 @@ import type {
|
||||
PlaygroundRequestSchema,
|
||||
} from 'openapi';
|
||||
import { StrategyExecution } from './StrategyExecution/StrategyExecution';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
||||
import { objectId } from 'utils/objectId';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { DisabledStrategyExecution } from './StrategyExecution/DisabledStrategyExecution';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/LegacyStrategyItemContainer';
|
||||
|
||||
interface IFeatureStrategyItemProps {
|
||||
strategy: PlaygroundStrategySchema;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Edit from '@mui/icons-material/Edit';
|
||||
@ -12,6 +11,7 @@ import {
|
||||
UPDATE_PROJECT,
|
||||
} from '@server/types/permissions';
|
||||
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/LegacyStrategyItemContainer';
|
||||
|
||||
interface ProjectEnvironmentDefaultStrategyProps {
|
||||
environment: ProjectEnvironmentType;
|
||||
|
@ -9,13 +9,11 @@ import {
|
||||
IconButton,
|
||||
FormHelperText,
|
||||
} from '@mui/material';
|
||||
import Delete from '@mui/icons-material/DeleteOutlined';
|
||||
import type { IReleasePlanMilestoneStrategy } from 'interfaces/releasePlans';
|
||||
import { type DragEventHandler, type RefObject, useRef, useState } from 'react';
|
||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||
import { MilestoneCardName } from './MilestoneCardName';
|
||||
import { MilestoneStrategyMenuCards } from './MilestoneStrategyMenu/MilestoneStrategyMenuCards';
|
||||
import { MilestoneStrategyDraggableItem } from './MilestoneStrategyDraggableItem';
|
||||
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
||||
import { ReleasePlanTemplateAddStrategyForm } from '../../MilestoneStrategy/ReleasePlanTemplateAddStrategyForm';
|
||||
import DragIndicator from '@mui/icons-material/DragIndicator';
|
||||
@ -26,6 +24,9 @@ import {
|
||||
StyledListItem,
|
||||
} from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
||||
import Edit from '@mui/icons-material/Edit';
|
||||
import Delete from '@mui/icons-material/DeleteOutlined';
|
||||
import { StrategyDraggableItem } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem';
|
||||
|
||||
const leftPadding = 3;
|
||||
|
||||
@ -449,19 +450,44 @@ export const MilestoneCard = ({
|
||||
<StyledListItem key={strg.id}>
|
||||
{index > 0 ? <StrategySeparator /> : null}
|
||||
|
||||
<MilestoneStrategyDraggableItem
|
||||
<StrategyDraggableItem
|
||||
index={index}
|
||||
onDragEnd={onStrategyDragEnd}
|
||||
onDragStartRef={onStrategyDragStartRef}
|
||||
onDragOver={onStrategyDragOver(strg.id)}
|
||||
onDeleteClick={() =>
|
||||
milestoneStrategyDeleted(strg.id)
|
||||
}
|
||||
onEditClick={() => {
|
||||
openAddUpdateStrategyForm(strg, true);
|
||||
}}
|
||||
isDragging={dragItem?.id === strg.id}
|
||||
strategy={strg}
|
||||
strategy={{
|
||||
...strg,
|
||||
name:
|
||||
strg.name ||
|
||||
strg.strategyName ||
|
||||
'',
|
||||
}}
|
||||
headerItemsRight={
|
||||
<>
|
||||
<IconButton
|
||||
title='Edit strategy'
|
||||
onClick={() => {
|
||||
openAddUpdateStrategyForm(
|
||||
strg,
|
||||
true,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Edit />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title='Remove strategy'
|
||||
onClick={() =>
|
||||
milestoneStrategyDeleted(
|
||||
strg.id,
|
||||
)
|
||||
}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</StyledListItem>
|
||||
))}
|
||||
|
Loading…
Reference in New Issue
Block a user