Skip to content

Commit

Permalink
chore: implement remaining logic
Browse files Browse the repository at this point in the history
  • Loading branch information
nunogois committed Nov 22, 2024
1 parent a217802 commit 3058f6d
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { IFeatureEnvironmentMetrics } from 'interfaces/featureToggle';
import { FeatureMetricsStats } from 'component/feature/FeatureView/FeatureMetrics/FeatureMetricsStats/FeatureMetricsStats';
import { SectionSeparator } from '../SectionSeparator/SectionSeparator';
import { styled } from '@mui/material';

const StyledLabel = styled('span')(({ theme }) => ({
background: theme.palette.envAccordion.expanded,
padding: theme.spacing(0, 2),
}));

interface IEnvironmentFooterProps {
environmentMetric?: IFeatureEnvironmentMetrics;
Expand All @@ -15,7 +21,9 @@ export const EnvironmentFooter = ({

return (
<>
<SectionSeparator>Feature flag exposure</SectionSeparator>
<SectionSeparator>
<StyledLabel>Feature flag exposure</StyledLabel>
</SectionSeparator>

<div>
<FeatureMetricsStats
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const SeparatorContent = styled('span')(({ theme }) => ({
fontSize: theme.fontSizes.bodySize,
textAlign: 'center',
padding: '0 1rem',
background: theme.palette.envAccordion.expanded,
position: 'relative',
maxWidth: '80%',
color: theme.palette.text.primary,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import { useReleasePlansApi } from 'hooks/api/actions/useReleasePlansApi/useRele
import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import useToast from 'hooks/useToast';
import type { ReleasePlan as TReleasePlan } from 'interfaces/releasePlans';
import type {
ReleasePlan as TReleasePlan,
ReleasePlanMilestone as TReleasePlanMilestone,
} from 'interfaces/releasePlans';
import { useState } from 'react';
import { formatUnknownError } from 'utils/formatUnknownError';
import { ReleasePlanRemoveDialog } from './ReleasePlanRemoveDialog';
import { ReleasePlanMilestone } from './ReleasePlanMilestone';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';

const StyledContainer = styled('div', {
shouldForwardProp: (prop) => prop !== 'disabled',
Expand Down Expand Up @@ -60,6 +64,13 @@ const StyledBody = styled('div')(({ theme }) => ({
marginTop: theme.spacing(3),
}));

const StyledConnection = styled('div')(({ theme }) => ({
width: 4,
height: theme.spacing(2),
backgroundColor: theme.palette.divider,
marginLeft: theme.spacing(3.25),
}));

interface IReleasePlanProps {
plan: TReleasePlan;
}
Expand All @@ -77,7 +88,8 @@ export const ReleasePlan = ({ plan }: IReleasePlanProps) => {

const projectId = useRequiredPathParam('projectId');
const { refetch } = useReleasePlans(projectId, featureName, environment);
const { removeReleasePlanFromFeature } = useReleasePlansApi();
const { removeReleasePlanFromFeature, startReleasePlanMilestone } =
useReleasePlansApi();
const { setToastData, setToastApiError } = useToast();

const [removeOpen, setRemoveOpen] = useState(false);
Expand All @@ -101,6 +113,25 @@ export const ReleasePlan = ({ plan }: IReleasePlanProps) => {
}
};

const onStartMilestone = async (milestone: TReleasePlanMilestone) => {
try {
await startReleasePlanMilestone(
projectId,
featureName,
environment,
id,
milestone.id,
);
setToastData({
title: `Milestone "${milestone.name}" has started`,
type: 'success',
});
refetch();
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};

const disabled = !activeMilestoneId;
const activeIndex = milestones.findIndex(
(milestone) => milestone.id === activeMilestoneId,
Expand Down Expand Up @@ -132,17 +163,23 @@ export const ReleasePlan = ({ plan }: IReleasePlanProps) => {
</StyledHeader>
<StyledBody>
{milestones.map((milestone, index) => (
<ReleasePlanMilestone
key={milestone.id}
milestone={milestone}
status={
index === activeIndex
? 'active'
: index < activeIndex
? 'completed'
: 'not-started'
}
/>
<div key={milestone.id}>
<ReleasePlanMilestone
milestone={milestone}
status={
index === activeIndex
? 'active'
: index < activeIndex
? 'completed'
: 'not-started'
}
onStartMilestone={onStartMilestone}
/>
<ConditionallyRender
condition={index < milestones.length - 1}
show={<StyledConnection />}
/>
</div>
))}
</StyledBody>
<ReleasePlanRemoveDialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import {
Accordion,
AccordionDetails,
AccordionSummary,
Link,
styled,
} from '@mui/material';
import PlayCircleIcon from '@mui/icons-material/PlayCircle';
import TripOriginIcon from '@mui/icons-material/TripOrigin';
import type { ReleasePlanMilestone as TReleasePlanMilestone } from 'interfaces/releasePlans';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ReleasePlanMilestoneStrategy } from './ReleasePlanMilestoneStrategy';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';

type MilestoneStatus = 'not-started' | 'active' | 'completed';

Expand All @@ -19,6 +22,9 @@ const StyledAccordion = styled(Accordion, {
boxShadow: 'none',
margin: 0,
backgroundColor: theme.palette.background.paper,
'&:before': {
display: 'none',
},
}));

const StyledAccordionSummary = styled(AccordionSummary)({
Expand Down Expand Up @@ -71,14 +77,22 @@ const StyledSecondaryLabel = styled('span')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
}));

const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
backgroundColor: theme.palette.envAccordion.expanded,
borderBottomLeftRadius: theme.shape.borderRadiusLarge,
borderBottomRightRadius: theme.shape.borderRadiusLarge,
}));

interface IReleasePlanMilestoneProps {
milestone: TReleasePlanMilestone;
status: MilestoneStatus;
onStartMilestone: (milestone: TReleasePlanMilestone) => void;
}

export const ReleasePlanMilestone = ({
milestone,
status,
onStartMilestone,
}: IReleasePlanMilestoneProps) => {
const statusText =
status === 'active'
Expand All @@ -98,15 +112,35 @@ export const ReleasePlanMilestone = ({
show={<TripOriginIcon />}
elseShow={<PlayCircleIcon />}
/>
<span>{statusText}</span>
<ConditionallyRender
condition={status === 'active'}
show={<span>{statusText}</span>}
elseShow={
<Link
onClick={(e) => {
e.stopPropagation();
onStartMilestone(milestone);
}}
>
{statusText}
</Link>
}
/>
</StyledStatus>
</StyledTitleContainer>
<StyledSecondaryLabel>View strategies</StyledSecondaryLabel>
</StyledAccordionSummary>
<AccordionDetails>
You'll be able to see the milestone strategies (
{milestone.strategies.length}) here sometime soon
</AccordionDetails>
<StyledAccordionDetails>
{milestone.strategies.map((strategy, index) => (
<div key={strategy.id}>
<ConditionallyRender
condition={index > 0}
show={<StrategySeparator text='OR' />}
/>
<ReleasePlanMilestoneStrategy strategy={strategy} />
</div>
))}
</StyledAccordionDetails>
</StyledAccordion>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Box, styled } from '@mui/material';
import { StrategyExecution } from '../FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
import {
formatStrategyName,
getFeatureStrategyIcon,
} from 'utils/strategyNames';
import type { IFeatureStrategy } from 'interfaces/strategy';

const StyledStrategy = styled('div')(({ theme }) => ({
background: theme.palette.background.paper,
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadiusMedium,
padding: theme.spacing(2),
}));

const StyledHeader = styled('div')(({ theme }) => ({
display: 'flex',
gap: theme.spacing(1),
alignItems: 'center',
color: theme.palette.text.primary,
'& > svg': {
fill: theme.palette.action.disabled,
},
marginBottom: theme.spacing(1),
}));

interface IReleasePlanMilestoneStrategyProps {
strategy: IFeatureStrategy;
}

export const ReleasePlanMilestoneStrategy = ({
strategy,
}: IReleasePlanMilestoneStrategyProps) => {
const Icon = getFeatureStrategyIcon(strategy.strategyName);

return (
<StyledStrategy>
<StyledHeader>
<Icon />
{`${formatStrategyName(String(strategy.strategyName))}${strategy.title ? `: ${strategy.title}` : ''}`}
</StyledHeader>
<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} />
))}
</StyledStrategy>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,23 @@ export const useReleasePlansApi = () => {
await makeRequest(req.caller, req.id);
};

const startReleasePlanMilestone = async (
projectId: string,
featureName: string,
environment: string,
releasePlanId: string,
milestoneId: string,
): Promise<void> => {
const requestId = 'startReleasePlanMilestone';
const path = `api/admin/projects/${projectId}/features/${featureName}/environments/${environment}/release_plans/${releasePlanId}/milestones/${milestoneId}/start`;
const req = createRequest(path, { method: 'POST' }, requestId);

await makeRequest(req.caller, req.id);
};

return {
addReleasePlanToFeature,
removeReleasePlanFromFeature,
startReleasePlanMilestone,
};
};
2 changes: 1 addition & 1 deletion frontend/src/utils/strategyNames.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const RolloutSvgIcon: FC = (props) => (
/>
);

export const getFeatureStrategyIcon = (strategyName: string) => {
export const getFeatureStrategyIcon = (strategyName?: string) => {
switch (strategyName) {
case 'default':
return PowerSettingsNewIcon;
Expand Down

0 comments on commit 3058f6d

Please sign in to comment.