mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
refactor: use union types for change request types (#5870)
This changes the two interfaces IChangeRequest and IChangeRequestSchedule to be union types instead of interfaces. It also extracts the constituents of those union types into proper types themselves (so that they can be used in function type signatures etc). It also updates the type names. This turned out to be more work than I had imagined, but I think the end result pays off, giving us more type safety and control. I wanted to use just `ChangeRequest` for the IChangeRequest type, but that caused issues due to naming collisions with the `ChangeRequest` component that we have, causing tests to fail. I've named it `ChangeRequestType` as a potential solution, but suggestions are welcome. The relevant changes are in `frontend/src/component/changeRequest/changeRequest.types.ts`. Everything else is updated references and some necessary refactoring to respect the new types.
This commit is contained in:
parent
6ba4591c7f
commit
39145e2617
@ -5,7 +5,7 @@ import { Route, Routes } from 'react-router-dom';
|
|||||||
import { render } from 'utils/testRenderer';
|
import { render } from 'utils/testRenderer';
|
||||||
import { ChangeRequest } from './ChangeRequest';
|
import { ChangeRequest } from './ChangeRequest';
|
||||||
import {
|
import {
|
||||||
IChangeRequest,
|
ChangeRequestType,
|
||||||
IChangeRequestAddStrategy,
|
IChangeRequestAddStrategy,
|
||||||
IChangeRequestEnabled,
|
IChangeRequestEnabled,
|
||||||
} from '../changeRequest.types';
|
} from '../changeRequest.types';
|
||||||
@ -98,7 +98,7 @@ const feature = () =>
|
|||||||
const changeRequestWithDefaultChange = (
|
const changeRequestWithDefaultChange = (
|
||||||
defaultChange: IChangeRequestEnabled | IChangeRequestAddStrategy,
|
defaultChange: IChangeRequestEnabled | IChangeRequestAddStrategy,
|
||||||
) => {
|
) => {
|
||||||
const changeRequest: IChangeRequest = {
|
const changeRequest: ChangeRequestType = {
|
||||||
approvals: [],
|
approvals: [],
|
||||||
rejections: [],
|
rejections: [],
|
||||||
comments: [],
|
comments: [],
|
||||||
@ -142,7 +142,7 @@ const changeRequestWithDefaultChange = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const changeRequest = (variants: StrategyVariantSchema[]) => {
|
const changeRequest = (variants: StrategyVariantSchema[]) => {
|
||||||
const changeRequest: IChangeRequest = {
|
const changeRequest: ChangeRequestType = {
|
||||||
approvals: [],
|
approvals: [],
|
||||||
rejections: [],
|
rejections: [],
|
||||||
comments: [],
|
comments: [],
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { VFC } from 'react';
|
import { VFC } from 'react';
|
||||||
import { Box, Typography } from '@mui/material';
|
import { Box, Typography } from '@mui/material';
|
||||||
import type { IChangeRequest } from '../changeRequest.types';
|
import type { ChangeRequestType } from '../changeRequest.types';
|
||||||
import { FeatureToggleChanges } from './Changes/FeatureToggleChanges';
|
import { FeatureToggleChanges } from './Changes/FeatureToggleChanges';
|
||||||
import { FeatureChange } from './Changes/Change/FeatureChange';
|
import { FeatureChange } from './Changes/Change/FeatureChange';
|
||||||
import { ChangeActions } from './Changes/Change/ChangeActions';
|
import { ChangeActions } from './Changes/Change/ChangeActions';
|
||||||
@ -8,7 +8,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { SegmentChange } from './Changes/Change/SegmentChange';
|
import { SegmentChange } from './Changes/Change/SegmentChange';
|
||||||
|
|
||||||
interface IChangeRequestProps {
|
interface IChangeRequestProps {
|
||||||
changeRequest: IChangeRequest;
|
changeRequest: ChangeRequestType;
|
||||||
onRefetch?: () => void;
|
onRefetch?: () => void;
|
||||||
onNavigate?: () => void;
|
onNavigate?: () => void;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { FC, useState } from 'react';
|
import React, { FC, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
IChangeRequest,
|
ChangeRequestType,
|
||||||
IChangeRequestAddStrategy,
|
IChangeRequestAddStrategy,
|
||||||
IChangeRequestUpdateStrategy,
|
IChangeRequestUpdateStrategy,
|
||||||
IChange,
|
IChange,
|
||||||
@ -28,7 +28,7 @@ import { EditChange } from './EditChange';
|
|||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
import { NewEditChange } from './NewEditChange';
|
import { NewEditChange } from './NewEditChange';
|
||||||
|
|
||||||
const useShowActions = (changeRequest: IChangeRequest, change: IChange) => {
|
const useShowActions = (changeRequest: ChangeRequestType, change: IChange) => {
|
||||||
const { isChangeRequestConfigured } = useChangeRequestsEnabled(
|
const { isChangeRequestConfigured } = useChangeRequestsEnabled(
|
||||||
changeRequest.project,
|
changeRequest.project,
|
||||||
);
|
);
|
||||||
@ -60,7 +60,7 @@ const StyledPopover = styled(Popover)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const ChangeActions: FC<{
|
export const ChangeActions: FC<{
|
||||||
changeRequest: IChangeRequest;
|
changeRequest: ChangeRequestType;
|
||||||
feature: string;
|
feature: string;
|
||||||
change: IChange;
|
change: IChange;
|
||||||
onRefetch?: () => void;
|
onRefetch?: () => void;
|
||||||
|
@ -3,6 +3,7 @@ import { screen } from '@testing-library/react';
|
|||||||
import { FeatureChange } from './FeatureChange';
|
import { FeatureChange } from './FeatureChange';
|
||||||
import {
|
import {
|
||||||
ChangeRequestState,
|
ChangeRequestState,
|
||||||
|
ChangeRequestType,
|
||||||
IChangeRequestFeature,
|
IChangeRequestFeature,
|
||||||
IFeatureChange,
|
IFeatureChange,
|
||||||
} from 'component/changeRequest/changeRequest.types';
|
} from 'component/changeRequest/changeRequest.types';
|
||||||
@ -40,21 +41,39 @@ describe('Schedule conflicts', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const changeRequest =
|
const changeRequest =
|
||||||
(feature: IChangeRequestFeature) => (state: ChangeRequestState) => ({
|
(feature: IChangeRequestFeature) =>
|
||||||
id: 1,
|
(state: ChangeRequestState): ChangeRequestType => {
|
||||||
state,
|
const shared = {
|
||||||
title: '',
|
id: 1,
|
||||||
project: 'default',
|
title: '',
|
||||||
environment: 'default',
|
project: 'default',
|
||||||
minApprovals: 1,
|
environment: 'default',
|
||||||
createdBy: { id: 1, username: 'user1', imageUrl: '' },
|
minApprovals: 1,
|
||||||
createdAt: new Date(),
|
createdBy: { id: 1, username: 'user1', imageUrl: '' },
|
||||||
features: [feature],
|
createdAt: new Date(),
|
||||||
segments: [],
|
features: [feature],
|
||||||
approvals: [],
|
segments: [],
|
||||||
rejections: [],
|
approvals: [],
|
||||||
comments: [],
|
rejections: [],
|
||||||
});
|
comments: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (state === 'Scheduled') {
|
||||||
|
return {
|
||||||
|
...shared,
|
||||||
|
state,
|
||||||
|
schedule: {
|
||||||
|
scheduledAt: '2024-01-12T09:46:51+05:30',
|
||||||
|
status: 'pending',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...shared,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
it.each(['Draft', 'Scheduled', 'In review', 'Approved'])(
|
it.each(['Draft', 'Scheduled', 'In review', 'Approved'])(
|
||||||
'should show schedule conflicts (when they exist) for change request in the %s state',
|
'should show schedule conflicts (when they exist) for change request in the %s state',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { FC, ReactNode } from 'react';
|
import { FC, ReactNode } from 'react';
|
||||||
import {
|
import {
|
||||||
IFeatureChange,
|
IFeatureChange,
|
||||||
IChangeRequest,
|
ChangeRequestType,
|
||||||
IChangeRequestFeature,
|
IChangeRequestFeature,
|
||||||
} from '../../../changeRequest.types';
|
} from '../../../changeRequest.types';
|
||||||
import { objectId } from 'utils/objectId';
|
import { objectId } from 'utils/objectId';
|
||||||
@ -69,7 +69,7 @@ const InlineList = styled('ul')(({ theme }) => ({
|
|||||||
export const FeatureChange: FC<{
|
export const FeatureChange: FC<{
|
||||||
actions: ReactNode;
|
actions: ReactNode;
|
||||||
index: number;
|
index: number;
|
||||||
changeRequest: IChangeRequest;
|
changeRequest: ChangeRequestType;
|
||||||
change: IFeatureChange;
|
change: IFeatureChange;
|
||||||
feature: IChangeRequestFeature;
|
feature: IChangeRequestFeature;
|
||||||
onNavigate?: () => void;
|
onNavigate?: () => void;
|
||||||
|
@ -2,7 +2,7 @@ import { Box } from '@mui/material';
|
|||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
import { Typography, Tooltip } from '@mui/material';
|
import { Typography, Tooltip } from '@mui/material';
|
||||||
import TimeAgo from 'react-timeago';
|
import TimeAgo from 'react-timeago';
|
||||||
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
import { ChangeRequestStatusBadge } from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge';
|
import { ChangeRequestStatusBadge } from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge';
|
||||||
import {
|
import {
|
||||||
StyledPaper,
|
StyledPaper,
|
||||||
@ -15,7 +15,7 @@ import { Separator } from '../../ChangeRequestSidebar/ChangeRequestSidebar';
|
|||||||
import { ChangeRequestTitle } from '../../ChangeRequestSidebar/EnvironmentChangeRequest/ChangeRequestTitle';
|
import { ChangeRequestTitle } from '../../ChangeRequestSidebar/EnvironmentChangeRequest/ChangeRequestTitle';
|
||||||
import { UpdateCount } from 'component/changeRequest/UpdateCount';
|
import { UpdateCount } from 'component/changeRequest/UpdateCount';
|
||||||
|
|
||||||
export const ChangeRequestHeader: FC<{ changeRequest: IChangeRequest }> = ({
|
export const ChangeRequestHeader: FC<{ changeRequest: ChangeRequestType }> = ({
|
||||||
changeRequest,
|
changeRequest,
|
||||||
}) => {
|
}) => {
|
||||||
const [title, setTitle] = useState(changeRequest.title);
|
const [title, setTitle] = useState(changeRequest.title);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { fireEvent, screen, waitFor, within } from '@testing-library/react';
|
import { fireEvent, screen, waitFor, within } from '@testing-library/react';
|
||||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||||
import { ChangeRequestState, IChangeRequest } from '../changeRequest.types';
|
import { ChangeRequestState, ChangeRequestType } from '../changeRequest.types';
|
||||||
import { render } from 'utils/testRenderer';
|
import { render } from 'utils/testRenderer';
|
||||||
import { ChangeRequestOverview } from './ChangeRequestOverview';
|
import { ChangeRequestOverview } from './ChangeRequestOverview';
|
||||||
import {
|
import {
|
||||||
@ -14,11 +14,10 @@ const mockChangeRequest = (
|
|||||||
featureName: string,
|
featureName: string,
|
||||||
state: ChangeRequestState,
|
state: ChangeRequestState,
|
||||||
failureReason?: string,
|
failureReason?: string,
|
||||||
): IChangeRequest => {
|
): ChangeRequestType => {
|
||||||
const result: IChangeRequest = {
|
const shared: Omit<ChangeRequestType, 'state' | 'schedule'> = {
|
||||||
id: 1,
|
id: 1,
|
||||||
environment: 'production',
|
environment: 'production',
|
||||||
state: state,
|
|
||||||
minApprovals: 1,
|
minApprovals: 1,
|
||||||
project: 'default',
|
project: 'default',
|
||||||
createdBy: {
|
createdBy: {
|
||||||
@ -60,26 +59,40 @@ const mockChangeRequest = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (state === 'Scheduled') {
|
if (state === 'Scheduled') {
|
||||||
result.schedule = {
|
if (failureReason) {
|
||||||
scheduledAt: '2022-12-02T09:19:12.242Z',
|
return {
|
||||||
status: 'pending',
|
...shared,
|
||||||
|
state,
|
||||||
|
schedule: {
|
||||||
|
scheduledAt: '2022-12-02T09:19:12.242Z',
|
||||||
|
status: 'failed',
|
||||||
|
reason: failureReason,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...shared,
|
||||||
|
state,
|
||||||
|
schedule: {
|
||||||
|
scheduledAt: '2022-12-02T09:19:12.242Z',
|
||||||
|
status: 'pending',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failureReason) {
|
return {
|
||||||
result.schedule!.failureReason = failureReason;
|
...shared,
|
||||||
}
|
state,
|
||||||
|
};
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
const pendingChangeRequest = (changeRequest: IChangeRequest) =>
|
const pendingChangeRequest = (changeRequest: ChangeRequestType) =>
|
||||||
testServerRoute(
|
testServerRoute(
|
||||||
server,
|
server,
|
||||||
'/api/admin/projects/default/change-requests/pending',
|
'/api/admin/projects/default/change-requests/pending',
|
||||||
[changeRequest],
|
[changeRequest],
|
||||||
);
|
);
|
||||||
|
|
||||||
const changeRequest = (changeRequest: IChangeRequest) =>
|
const changeRequest = (changeRequest: ChangeRequestType) =>
|
||||||
testServerRoute(
|
testServerRoute(
|
||||||
server,
|
server,
|
||||||
'/api/admin/projects/default/change-requests/1',
|
'/api/admin/projects/default/change-requests/1',
|
||||||
|
@ -111,7 +111,9 @@ export const ChangeRequestOverview: FC = () => {
|
|||||||
changeRequest.environment,
|
changeRequest.environment,
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasSchedule = Boolean(changeRequest.schedule?.scheduledAt);
|
const hasSchedule = Boolean(
|
||||||
|
'schedule' in changeRequest && changeRequest.schedule?.scheduledAt,
|
||||||
|
);
|
||||||
|
|
||||||
const onApplyChanges = async () => {
|
const onApplyChanges = async () => {
|
||||||
try {
|
try {
|
||||||
@ -259,6 +261,30 @@ export const ChangeRequestOverview: FC = () => {
|
|||||||
|
|
||||||
const countOfChanges = changesCount(changeRequest);
|
const countOfChanges = changesCount(changeRequest);
|
||||||
|
|
||||||
|
const reason = (() => {
|
||||||
|
if (!('schedule' in changeRequest)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (changeRequest.schedule.status) {
|
||||||
|
case 'failed':
|
||||||
|
return (
|
||||||
|
(changeRequest.schedule.reason ||
|
||||||
|
changeRequest.schedule.failureReason) ??
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
case 'suspended':
|
||||||
|
return changeRequest.schedule.reason;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
const scheduledAt =
|
||||||
|
'schedule' in changeRequest
|
||||||
|
? changeRequest.schedule.scheduledAt
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ChangeRequestHeader changeRequest={changeRequest} />
|
<ChangeRequestHeader changeRequest={changeRequest} />
|
||||||
@ -266,8 +292,12 @@ export const ChangeRequestOverview: FC = () => {
|
|||||||
<StyledAsideBox>
|
<StyledAsideBox>
|
||||||
<ChangeRequestTimeline
|
<ChangeRequestTimeline
|
||||||
state={changeRequest.state}
|
state={changeRequest.state}
|
||||||
scheduledAt={changeRequest.schedule?.scheduledAt}
|
scheduledAt={
|
||||||
failureReason={changeRequest.schedule?.failureReason}
|
'schedule' in changeRequest
|
||||||
|
? changeRequest.schedule.scheduledAt
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
failureReason={reason}
|
||||||
/>
|
/>
|
||||||
<ChangeRequestReviewers changeRequest={changeRequest} />
|
<ChangeRequestReviewers changeRequest={changeRequest} />
|
||||||
</StyledAsideBox>
|
</StyledAsideBox>
|
||||||
@ -417,10 +447,7 @@ export const ChangeRequestOverview: FC = () => {
|
|||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={
|
condition={
|
||||||
scheduleChangeRequests &&
|
scheduleChangeRequests &&
|
||||||
Boolean(
|
Boolean(scheduledAt)
|
||||||
changeRequest.schedule
|
|
||||||
?.scheduledAt,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
show={
|
show={
|
||||||
<StyledButton
|
<StyledButton
|
||||||
@ -483,27 +510,22 @@ export const ChangeRequestOverview: FC = () => {
|
|||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
environment={changeRequest.environment}
|
environment={changeRequest.environment}
|
||||||
primaryButtonText={
|
primaryButtonText={
|
||||||
changeRequest?.schedule?.scheduledAt
|
changeRequest.state === 'Scheduled'
|
||||||
? 'Update scheduled time'
|
? 'Update scheduled time'
|
||||||
: 'Schedule changes'
|
: 'Schedule changes'
|
||||||
}
|
}
|
||||||
title={
|
title={
|
||||||
changeRequest?.schedule?.scheduledAt
|
changeRequest.state === 'Scheduled'
|
||||||
? 'Update schedule'
|
? 'Update schedule'
|
||||||
: 'Schedule changes'
|
: 'Schedule changes'
|
||||||
}
|
}
|
||||||
scheduledAt={
|
scheduledAt={scheduledAt}
|
||||||
changeRequest?.schedule?.scheduledAt ||
|
|
||||||
undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ChangeRequestApplyScheduledDialogue
|
<ChangeRequestApplyScheduledDialogue
|
||||||
open={showApplyScheduledDialog}
|
open={showApplyScheduledDialog}
|
||||||
onConfirm={onApplyChanges}
|
onConfirm={onApplyChanges}
|
||||||
onClose={onApplyScheduledAbort}
|
onClose={onApplyScheduledAbort}
|
||||||
scheduledTime={
|
scheduledTime={scheduledAt}
|
||||||
changeRequest?.schedule?.scheduledAt
|
|
||||||
}
|
|
||||||
disabled={!allowChangeRequestActions || loading}
|
disabled={!allowChangeRequestActions || loading}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
environment={changeRequest.environment}
|
environment={changeRequest.environment}
|
||||||
@ -512,9 +534,7 @@ export const ChangeRequestOverview: FC = () => {
|
|||||||
open={showRejectScheduledDialog}
|
open={showRejectScheduledDialog}
|
||||||
onConfirm={onReject}
|
onConfirm={onReject}
|
||||||
onClose={onRejectScheduledAbort}
|
onClose={onRejectScheduledAbort}
|
||||||
scheduledTime={
|
scheduledTime={scheduledAt}
|
||||||
changeRequest?.schedule?.scheduledAt
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -26,15 +26,16 @@ import {
|
|||||||
} from './ChangeRequestReviewStatus.styles';
|
} from './ChangeRequestReviewStatus.styles';
|
||||||
import {
|
import {
|
||||||
ChangeRequestState,
|
ChangeRequestState,
|
||||||
IChangeRequest,
|
ChangeRequestType,
|
||||||
IChangeRequestSchedule,
|
ChangeRequestSchedule,
|
||||||
|
ChangeRequestScheduleFailed,
|
||||||
} from 'component/changeRequest/changeRequest.types';
|
} from 'component/changeRequest/changeRequest.types';
|
||||||
import { getBrowserTimezone } from './utils';
|
import { getBrowserTimezone } from './utils';
|
||||||
import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender';
|
||||||
import { formatDateYMDHMS } from 'utils/formatDate';
|
import { formatDateYMDHMS } from 'utils/formatDate';
|
||||||
|
|
||||||
interface ISuggestChangeReviewsStatusProps {
|
interface ISuggestChangeReviewsStatusProps {
|
||||||
changeRequest: IChangeRequest;
|
changeRequest: ChangeRequestType;
|
||||||
onEditClick?: () => void;
|
onEditClick?: () => void;
|
||||||
}
|
}
|
||||||
const resolveBorder = (state: ChangeRequestState, theme: Theme) => {
|
const resolveBorder = (state: ChangeRequestState, theme: Theme) => {
|
||||||
@ -103,7 +104,7 @@ export const ChangeRequestReviewStatus: FC<ISuggestChangeReviewsStatusProps> =
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface IResolveComponentProps {
|
interface IResolveComponentProps {
|
||||||
changeRequest: IChangeRequest;
|
changeRequest: ChangeRequestType;
|
||||||
onEditClick?: () => void;
|
onEditClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ const ResolveComponent = ({
|
|||||||
changeRequest,
|
changeRequest,
|
||||||
onEditClick,
|
onEditClick,
|
||||||
}: IResolveComponentProps) => {
|
}: IResolveComponentProps) => {
|
||||||
const { state, schedule } = changeRequest;
|
const { state } = changeRequest;
|
||||||
|
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return null;
|
return null;
|
||||||
@ -134,6 +135,7 @@ const ResolveComponent = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state === 'Scheduled') {
|
if (state === 'Scheduled') {
|
||||||
|
const { schedule } = changeRequest;
|
||||||
return <Scheduled schedule={schedule} onEditClick={onEditClick} />;
|
return <Scheduled schedule={schedule} onEditClick={onEditClick} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,13 +230,11 @@ const StyledIconButton = styled(IconButton)({
|
|||||||
});
|
});
|
||||||
|
|
||||||
interface IScheduledProps {
|
interface IScheduledProps {
|
||||||
schedule?: IChangeRequest['schedule'];
|
schedule?: ChangeRequestSchedule;
|
||||||
onEditClick?: () => any;
|
onEditClick?: () => any;
|
||||||
}
|
}
|
||||||
const Scheduled = ({ schedule, onEditClick }: IScheduledProps) => {
|
const Scheduled = ({ schedule, onEditClick }: IScheduledProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const timezone = getBrowserTimezone();
|
|
||||||
const { locationSettings } = useLocationSettings();
|
|
||||||
|
|
||||||
if (!schedule?.scheduledAt) {
|
if (!schedule?.scheduledAt) {
|
||||||
return null;
|
return null;
|
||||||
@ -258,9 +258,13 @@ const Scheduled = ({ schedule, onEditClick }: IScheduledProps) => {
|
|||||||
|
|
||||||
<StyledScheduledBox>
|
<StyledScheduledBox>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={schedule?.status === 'pending'}
|
condition={schedule.status === 'pending'}
|
||||||
show={<ScheduledPending schedule={schedule} />}
|
show={<ScheduledPending schedule={schedule} />}
|
||||||
elseShow={<ScheduledFailed schedule={schedule} />}
|
elseShow={
|
||||||
|
<ScheduledFailed
|
||||||
|
schedule={schedule as ChangeRequestScheduleFailed}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<StyledIconButton onClick={onEditClick}>
|
<StyledIconButton onClick={onEditClick}>
|
||||||
@ -273,7 +277,7 @@ const Scheduled = ({ schedule, onEditClick }: IScheduledProps) => {
|
|||||||
|
|
||||||
const ScheduledFailed = ({
|
const ScheduledFailed = ({
|
||||||
schedule,
|
schedule,
|
||||||
}: { schedule: IChangeRequestSchedule }) => {
|
}: { schedule: ChangeRequestScheduleFailed }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const timezone = getBrowserTimezone();
|
const timezone = getBrowserTimezone();
|
||||||
const { locationSettings } = useLocationSettings();
|
const { locationSettings } = useLocationSettings();
|
||||||
@ -293,7 +297,7 @@ const ScheduledFailed = ({
|
|||||||
<Box>
|
<Box>
|
||||||
<StyledReviewTitle color={theme.palette.error.main}>
|
<StyledReviewTitle color={theme.palette.error.main}>
|
||||||
Changes failed to be applied on {scheduledTime} because of{' '}
|
Changes failed to be applied on {scheduledTime} because of{' '}
|
||||||
{schedule?.failureReason}
|
{schedule.reason ?? schedule.failureReason}
|
||||||
</StyledReviewTitle>
|
</StyledReviewTitle>
|
||||||
<Typography>Your timezone is {timezone}</Typography>
|
<Typography>Your timezone is {timezone}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -303,7 +307,7 @@ const ScheduledFailed = ({
|
|||||||
|
|
||||||
const ScheduledPending = ({
|
const ScheduledPending = ({
|
||||||
schedule,
|
schedule,
|
||||||
}: { schedule: IChangeRequestSchedule }) => {
|
}: { schedule: ChangeRequestSchedule }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const timezone = getBrowserTimezone();
|
const timezone = getBrowserTimezone();
|
||||||
const { locationSettings } = useLocationSettings();
|
const { locationSettings } = useLocationSettings();
|
||||||
|
@ -3,7 +3,7 @@ import { FC, ReactNode } from 'react';
|
|||||||
import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender';
|
||||||
import { ChangeRequestRejections } from './ChangeRequestRejections';
|
import { ChangeRequestRejections } from './ChangeRequestRejections';
|
||||||
import { ChangeRequestApprovals } from './ChangeRequestApprovals';
|
import { ChangeRequestApprovals } from './ChangeRequestApprovals';
|
||||||
import { IChangeRequest } from '../../changeRequest.types';
|
import { ChangeRequestType } from '../../changeRequest.types';
|
||||||
|
|
||||||
const StyledBox = styled(Box)(({ theme }) => ({
|
const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
marginBottom: theme.spacing(2),
|
marginBottom: theme.spacing(2),
|
||||||
@ -44,7 +44,7 @@ export const ChangeRequestReviewersWrapper: FC<{ header: ReactNode }> = ({
|
|||||||
|
|
||||||
export const ChangeRequestReviewers: FC<{
|
export const ChangeRequestReviewers: FC<{
|
||||||
changeRequest: Pick<
|
changeRequest: Pick<
|
||||||
IChangeRequest,
|
ChangeRequestType,
|
||||||
'approvals' | 'rejections' | 'state' | 'minApprovals'
|
'approvals' | 'rejections' | 'state' | 'minApprovals'
|
||||||
>;
|
>;
|
||||||
}> = ({ changeRequest }) => (
|
}> = ({ changeRequest }) => (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { FC, useState } from 'react';
|
import React, { FC, useState } from 'react';
|
||||||
import { Box, Button, IconButton, styled, Typography } from '@mui/material';
|
import { Box, Button, IconButton, styled, Typography } from '@mui/material';
|
||||||
import Input from 'component/common/Input/Input';
|
import Input from 'component/common/Input/Input';
|
||||||
import { IChangeRequest } from '../../changeRequest.types';
|
import { ChangeRequestType } from '../../changeRequest.types';
|
||||||
import { Edit } from '@mui/icons-material';
|
import { Edit } from '@mui/icons-material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
||||||
@ -25,7 +25,7 @@ export const StyledHeader = styled(Typography)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const ChangeRequestTitle: FC<{
|
export const ChangeRequestTitle: FC<{
|
||||||
environmentChangeRequest: IChangeRequest;
|
environmentChangeRequest: ChangeRequestType;
|
||||||
title: string;
|
title: string;
|
||||||
setTitle: React.Dispatch<React.SetStateAction<string>>;
|
setTitle: React.Dispatch<React.SetStateAction<string>>;
|
||||||
}> = ({ environmentChangeRequest, title, setTitle, children }) => {
|
}> = ({ environmentChangeRequest, title, setTitle, children }) => {
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { IChangeRequest } from '../../changeRequest.types';
|
import { ChangeRequestType } from '../../changeRequest.types';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { ChangeRequestStatusBadge } from '../../ChangeRequestStatusBadge/ChangeRequestStatusBadge';
|
import { ChangeRequestStatusBadge } from '../../ChangeRequestStatusBadge/ChangeRequestStatusBadge';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
@ -54,7 +54,7 @@ const ChangeRequestContent = styled(Box)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const EnvironmentChangeRequest: FC<{
|
export const EnvironmentChangeRequest: FC<{
|
||||||
environmentChangeRequest: IChangeRequest;
|
environmentChangeRequest: ChangeRequestType;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onReview: (id: number, comment?: string) => void;
|
onReview: (id: number, comment?: string) => void;
|
||||||
onDiscard: (id: number) => void;
|
onDiscard: (id: number) => void;
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
import { screen } from '@testing-library/react';
|
import { screen } from '@testing-library/react';
|
||||||
import { ChangeRequestTitle } from './ChangeRequestTitle';
|
import { ChangeRequestTitle } from './ChangeRequestTitle';
|
||||||
import { ChangeRequestState } from 'component/changeRequest/changeRequest.types';
|
import { UnscheduledChangeRequest } from 'component/changeRequest/changeRequest.types';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||||
import { render } from 'utils/testRenderer';
|
import { render } from 'utils/testRenderer';
|
||||||
|
|
||||||
const changeRequest = {
|
const changeRequest: UnscheduledChangeRequest = {
|
||||||
id: 3,
|
id: 3,
|
||||||
state: 'Draft' as ChangeRequestState,
|
state: 'Draft' as const,
|
||||||
title: 'My title',
|
title: 'My title',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
environment: 'default',
|
environment: 'default',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { VFC } from 'react';
|
import { VFC } from 'react';
|
||||||
import { IChangeRequest } from '../changeRequest.types';
|
import { ChangeRequestType } from '../changeRequest.types';
|
||||||
import { Badge } from 'component/common/Badge/Badge';
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
import {
|
import {
|
||||||
AccessTime,
|
AccessTime,
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||||
|
|
||||||
interface IChangeRequestStatusBadgeProps {
|
interface IChangeRequestStatusBadgeProps {
|
||||||
changeRequest: IChangeRequest | undefined;
|
changeRequest: ChangeRequestType | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReviewRequiredBadge: VFC = () => (
|
const ReviewRequiredBadge: VFC = () => (
|
||||||
@ -71,12 +71,18 @@ export const ChangeRequestStatusBadge: VFC<IChangeRequestStatusBadgeProps> = ({
|
|||||||
schedule!.scheduledAt,
|
schedule!.scheduledAt,
|
||||||
).toLocaleString();
|
).toLocaleString();
|
||||||
|
|
||||||
const tooltipTitle =
|
const tooltipTitle = (() => {
|
||||||
schedule?.status === 'pending'
|
switch (schedule.status) {
|
||||||
? `Scheduled for ${scheduledAt}`
|
case 'failed':
|
||||||
: `Failed on ${scheduledAt} because of ${
|
return `Failed on ${scheduledAt} because of ${
|
||||||
schedule!.failureReason
|
schedule.reason || schedule.failureReason
|
||||||
}`;
|
}`;
|
||||||
|
case 'suspended':
|
||||||
|
return schedule.reason;
|
||||||
|
default:
|
||||||
|
return `Scheduled for ${scheduledAt}`;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HtmlTooltip title={tooltipTitle} arrow>
|
<HtmlTooltip title={tooltipTitle} arrow>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { VFC } from 'react';
|
import { VFC } from 'react';
|
||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
import { ChangeRequestStatusBadge } from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge';
|
import { ChangeRequestStatusBadge } from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge';
|
||||||
|
|
||||||
interface IChangeRequestStatusCellProps {
|
interface IChangeRequestStatusCellProps {
|
||||||
value?: string | null; // FIXME: proper type
|
value?: string | null; // FIXME: proper type
|
||||||
row: { original: IChangeRequest };
|
row: { original: ChangeRequestType };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChangeRequestStatusCell: VFC<IChangeRequestStatusCellProps> = ({
|
export const ChangeRequestStatusCell: VFC<IChangeRequestStatusCellProps> = ({
|
||||||
|
@ -3,7 +3,7 @@ import { IFeatureStrategy } from '../../interfaces/strategy';
|
|||||||
import { IUser } from '../../interfaces/user';
|
import { IUser } from '../../interfaces/user';
|
||||||
import { SetStrategySortOrderSchema } from '../../openapi';
|
import { SetStrategySortOrderSchema } from '../../openapi';
|
||||||
|
|
||||||
export interface IChangeRequest {
|
type BaseChangeRequest = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
project: string;
|
project: string;
|
||||||
@ -17,15 +17,43 @@ export interface IChangeRequest {
|
|||||||
rejections: IChangeRequestApproval[];
|
rejections: IChangeRequestApproval[];
|
||||||
comments: IChangeRequestComment[];
|
comments: IChangeRequestComment[];
|
||||||
conflict?: string;
|
conflict?: string;
|
||||||
state: ChangeRequestState;
|
};
|
||||||
schedule?: IChangeRequestSchedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IChangeRequestSchedule {
|
export type UnscheduledChangeRequest = BaseChangeRequest & {
|
||||||
|
state: Exclude<ChangeRequestState, 'Scheduled'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ScheduledChangeRequest = BaseChangeRequest & {
|
||||||
|
state: 'Scheduled';
|
||||||
|
schedule: ChangeRequestSchedule;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChangeRequestType =
|
||||||
|
| UnscheduledChangeRequest
|
||||||
|
| ScheduledChangeRequest;
|
||||||
|
|
||||||
|
export type ChangeRequestSchedulePending = {
|
||||||
|
status: 'pending';
|
||||||
scheduledAt: string;
|
scheduledAt: string;
|
||||||
status: 'pending' | 'failed';
|
};
|
||||||
failureReason?: string;
|
|
||||||
}
|
export type ChangeRequestScheduleFailed = {
|
||||||
|
status: 'failed';
|
||||||
|
scheduledAt: string;
|
||||||
|
failureReason?: string | null;
|
||||||
|
reason: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChangeRequestScheduleSuspended = {
|
||||||
|
status: 'suspended';
|
||||||
|
scheduledAt: string;
|
||||||
|
reason: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChangeRequestSchedule =
|
||||||
|
| ChangeRequestSchedulePending
|
||||||
|
| ChangeRequestScheduleFailed
|
||||||
|
| ChangeRequestScheduleSuspended;
|
||||||
|
|
||||||
export interface IChangeRequestEnvironmentConfig {
|
export interface IChangeRequestEnvironmentConfig {
|
||||||
environment: string;
|
environment: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { IChangeRequest } from './changeRequest.types';
|
import { ChangeRequestType } from './changeRequest.types';
|
||||||
|
|
||||||
export const changesCount = (changeRequest: IChangeRequest) =>
|
export const changesCount = (changeRequest: ChangeRequestType) =>
|
||||||
changeRequest.features.flatMap((feature) => feature.changes).length +
|
changeRequest.features.flatMap((feature) => feature.changes).length +
|
||||||
changeRequest.segments.length;
|
changeRequest.segments.length;
|
||||||
|
@ -6,7 +6,7 @@ import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
|||||||
import { screen } from '@testing-library/dom';
|
import { screen } from '@testing-library/dom';
|
||||||
import { Route, Routes } from 'react-router-dom';
|
import { Route, Routes } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
IChangeRequest,
|
ChangeRequestType,
|
||||||
ChangeRequestAction,
|
ChangeRequestAction,
|
||||||
} from 'component/changeRequest/changeRequest.types';
|
} from 'component/changeRequest/changeRequest.types';
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ const strategy = {
|
|||||||
const draftRequest = (
|
const draftRequest = (
|
||||||
action: Omit<ChangeRequestAction, 'updateSegment'> = 'updateStrategy',
|
action: Omit<ChangeRequestAction, 'updateSegment'> = 'updateStrategy',
|
||||||
createdBy = 1,
|
createdBy = 1,
|
||||||
): IChangeRequest => {
|
): ChangeRequestType => {
|
||||||
return {
|
return {
|
||||||
id: 71,
|
id: 71,
|
||||||
title: 'Change request #71',
|
title: 'Change request #71',
|
||||||
|
@ -6,7 +6,7 @@ import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
|||||||
import { arraysHaveSameItems } from 'utils/arraysHaveSameItems';
|
import { arraysHaveSameItems } from 'utils/arraysHaveSameItems';
|
||||||
import { Alert, List, ListItem, styled } from '@mui/material';
|
import { Alert, List, ListItem, styled } from '@mui/material';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ interface IFeatureSettingsProjectConfirm {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onClick: (args: any) => void;
|
onClick: (args: any) => void;
|
||||||
feature: IFeatureToggle;
|
feature: IFeatureToggle;
|
||||||
changeRequests: IChangeRequest[] | undefined;
|
changeRequests: ChangeRequestType[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeatureSettingsProjectConfirm = ({
|
const FeatureSettingsProjectConfirm = ({
|
||||||
|
@ -5,7 +5,7 @@ import { screen } from '@testing-library/dom';
|
|||||||
import { Route, Routes } from 'react-router-dom';
|
import { Route, Routes } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
ChangeRequestAction,
|
ChangeRequestAction,
|
||||||
IChangeRequest,
|
ChangeRequestType,
|
||||||
} from 'component/changeRequest/changeRequest.types';
|
} from 'component/changeRequest/changeRequest.types';
|
||||||
import { EnvironmentVariantsCard } from './EnvironmentVariantsCard';
|
import { EnvironmentVariantsCard } from './EnvironmentVariantsCard';
|
||||||
import { IFeatureEnvironment } from 'interfaces/featureToggle';
|
import { IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||||
@ -30,7 +30,7 @@ const strategy = {
|
|||||||
const scheduledRequest = (
|
const scheduledRequest = (
|
||||||
action: Omit<ChangeRequestAction, 'updateSegment'> = 'updateStrategy',
|
action: Omit<ChangeRequestAction, 'updateSegment'> = 'updateStrategy',
|
||||||
createdBy = 1,
|
createdBy = 1,
|
||||||
): IChangeRequest => {
|
): ChangeRequestType => {
|
||||||
return {
|
return {
|
||||||
id: 71,
|
id: 71,
|
||||||
title: 'Change request #71',
|
title: 'Change request #71',
|
||||||
|
@ -3,7 +3,7 @@ import { Box, Button, styled, Typography } from '@mui/material';
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { ChangeRequestSidebar } from 'component/changeRequest/ChangeRequestSidebar/ChangeRequestSidebar';
|
import { ChangeRequestSidebar } from 'component/changeRequest/ChangeRequestSidebar/ChangeRequestSidebar';
|
||||||
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
|
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
|
||||||
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
import { changesCount } from 'component/changeRequest/changesCount';
|
import { changesCount } from 'component/changeRequest/changesCount';
|
||||||
import { Sticky } from 'component/common/Sticky/Sticky';
|
import { Sticky } from 'component/common/Sticky/Sticky';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
@ -61,7 +61,7 @@ const StyledSpaciousDraftBanner = styled(Box)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const DraftBannerContent: FC<{
|
const DraftBannerContent: FC<{
|
||||||
changeRequests: IChangeRequest[];
|
changeRequests: ChangeRequestType[];
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}> = ({ changeRequests, onClick }) => {
|
}> = ({ changeRequests, onClick }) => {
|
||||||
const environments = changeRequests.map(({ environment }) => environment);
|
const environments = changeRequests.map(({ environment }) => environment);
|
||||||
@ -165,7 +165,7 @@ export const DraftBanner: VFC<IDraftBannerProps> = ({ project }) => {
|
|||||||
show={
|
show={
|
||||||
<DraftBannerContent
|
<DraftBannerContent
|
||||||
changeRequests={
|
changeRequests={
|
||||||
unfinishedChangeRequests as IChangeRequest[]
|
unfinishedChangeRequests as ChangeRequestType[]
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsSidebarOpen(true);
|
setIsSidebarOpen(true);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import useLoading from 'hooks/useLoading';
|
import useLoading from 'hooks/useLoading';
|
||||||
import { Box, styled, Typography } from '@mui/material';
|
import { Box, styled, Typography } from '@mui/material';
|
||||||
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
StyledCount,
|
StyledCount,
|
||||||
@ -74,10 +74,12 @@ export const ChangeRequestsWidget: FC<IChangeRequestsWidgetProps> = ({
|
|||||||
const { changeRequests, loading } = useProjectChangeRequests(projectId);
|
const { changeRequests, loading } = useProjectChangeRequests(projectId);
|
||||||
const loadingRef = useLoading(loading, `[data-loading="${LOADING_LABEL}"]`);
|
const loadingRef = useLoading(loading, `[data-loading="${LOADING_LABEL}"]`);
|
||||||
const toBeApplied = changeRequests?.filter(
|
const toBeApplied = changeRequests?.filter(
|
||||||
(changeRequest: IChangeRequest) => changeRequest?.state === 'Approved',
|
(changeRequest: ChangeRequestType) =>
|
||||||
|
changeRequest?.state === 'Approved',
|
||||||
).length;
|
).length;
|
||||||
const toBeReviewed = changeRequests?.filter(
|
const toBeReviewed = changeRequests?.filter(
|
||||||
(changeRequest: IChangeRequest) => changeRequest?.state === 'In review',
|
(changeRequest: ChangeRequestType) =>
|
||||||
|
changeRequest?.state === 'In review',
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
|
|
||||||
export const useChangeRequest = (projectId: string, id: string) => {
|
export const useChangeRequest = (projectId: string, id: string) => {
|
||||||
const { data, error, mutate } = useSWR<IChangeRequest>(
|
const { data, error, mutate } = useSWR<ChangeRequestType>(
|
||||||
formatApiPath(`api/admin/projects/${projectId}/change-requests/${id}`),
|
formatApiPath(`api/admin/projects/${projectId}/change-requests/${id}`),
|
||||||
fetcher,
|
fetcher,
|
||||||
{ refreshInterval: 15000 },
|
{ refreshInterval: 15000 },
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
import { useEnterpriseSWR } from '../useEnterpriseSWR/useEnterpriseSWR';
|
import { useEnterpriseSWR } from '../useEnterpriseSWR/useEnterpriseSWR';
|
||||||
|
|
||||||
const fetcher = (path: string) => {
|
const fetcher = (path: string) => {
|
||||||
@ -10,7 +10,7 @@ const fetcher = (path: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const usePendingChangeRequests = (project: string) => {
|
export const usePendingChangeRequests = (project: string) => {
|
||||||
const { data, error, mutate } = useEnterpriseSWR<IChangeRequest[]>(
|
const { data, error, mutate } = useEnterpriseSWR<ChangeRequestType[]>(
|
||||||
[],
|
[],
|
||||||
formatApiPath(`api/admin/projects/${project}/change-requests/pending`),
|
formatApiPath(`api/admin/projects/${project}/change-requests/pending`),
|
||||||
fetcher,
|
fetcher,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||||
import { useEnterpriseSWR } from '../useEnterpriseSWR/useEnterpriseSWR';
|
import { useEnterpriseSWR } from '../useEnterpriseSWR/useEnterpriseSWR';
|
||||||
|
|
||||||
const fetcher = (path: string) => {
|
const fetcher = (path: string) => {
|
||||||
@ -13,7 +13,7 @@ export const usePendingChangeRequestsForFeature = (
|
|||||||
project: string,
|
project: string,
|
||||||
featureName: string,
|
featureName: string,
|
||||||
) => {
|
) => {
|
||||||
const { data, error, mutate } = useEnterpriseSWR<IChangeRequest[]>(
|
const { data, error, mutate } = useEnterpriseSWR<ChangeRequestType[]>(
|
||||||
[],
|
[],
|
||||||
formatApiPath(
|
formatApiPath(
|
||||||
`api/admin/projects/${project}/change-requests/pending/${featureName}`,
|
`api/admin/projects/${project}/change-requests/pending/${featureName}`,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Alert } from '@mui/material';
|
import { Alert } from '@mui/material';
|
||||||
import { oneOf } from 'utils/oneOf';
|
import { oneOf } from 'utils/oneOf';
|
||||||
import { IChangeRequest } from '../component/changeRequest/changeRequest.types';
|
import { ChangeRequestType } from '../component/changeRequest/changeRequest.types';
|
||||||
|
|
||||||
export const useChangeRequestInReviewWarning = (
|
export const useChangeRequestInReviewWarning = (
|
||||||
draft: IChangeRequest[] | undefined,
|
draft: ChangeRequestType[] | undefined,
|
||||||
) => {
|
) => {
|
||||||
const changeRequestInReviewOrApproved = (environment: string) => {
|
const changeRequestInReviewOrApproved = (environment: string) => {
|
||||||
if (!draft) return false;
|
if (!draft) return false;
|
||||||
|
Loading…
Reference in New Issue
Block a user