mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-18 01:18:23 +02:00
chore: track metrics for how many CRs are moved into next state with conflicts (#6109)
Use React's context to track how many CRs are moved into their next state with conflicts present. This PR wraps environment change requests and change request overviews in a change request plausible context that contains a `willOverwriteStrategyChanges` property. This property is updated by the diff calculation if there are any conflicts and then read by the `changeState` function in the `useChangeRequestApi` hook. As long as at least one of the strategies in the CR contain conflicts, it will be marked as overwriting changes.
This commit is contained in:
parent
1d18187f7d
commit
73c4c62ea3
@ -1,8 +1,10 @@
|
|||||||
import { Box, styled } from '@mui/material';
|
import { Box, styled } from '@mui/material';
|
||||||
import { IChangeRequestUpdateStrategy } from 'component/changeRequest/changeRequest.types';
|
import { IChangeRequestUpdateStrategy } from 'component/changeRequest/changeRequest.types';
|
||||||
|
import { useChangeRequestPlausibleContext } from 'component/changeRequest/ChangeRequestContext';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||||
import { getChangesThatWouldBeOverwritten } from './strategy-change-diff-calculation';
|
import { getChangesThatWouldBeOverwritten } from './strategy-change-diff-calculation';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
const ChangesToOverwriteWarning = styled(Box)(({ theme }) => ({
|
const ChangesToOverwriteWarning = styled(Box)(({ theme }) => ({
|
||||||
color: theme.palette.warning.dark,
|
color: theme.palette.warning.dark,
|
||||||
@ -76,6 +78,14 @@ export const ChangesToOverwrite: React.FC<{
|
|||||||
const changesThatWouldBeOverwritten = checkForChanges
|
const changesThatWouldBeOverwritten = checkForChanges
|
||||||
? getChangesThatWouldBeOverwritten(currentStrategy, change)
|
? getChangesThatWouldBeOverwritten(currentStrategy, change)
|
||||||
: null;
|
: null;
|
||||||
|
const { registerWillOverwriteStrategyChanges } =
|
||||||
|
useChangeRequestPlausibleContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (changesThatWouldBeOverwritten) {
|
||||||
|
registerWillOverwriteStrategyChanges();
|
||||||
|
}
|
||||||
|
}, [changesThatWouldBeOverwritten]);
|
||||||
|
|
||||||
if (!changesThatWouldBeOverwritten) {
|
if (!changesThatWouldBeOverwritten) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
const defaultContext = {
|
||||||
|
willOverwriteStrategyChanges: false,
|
||||||
|
registerWillOverwriteStrategyChanges: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChangeRequestPlausibleContext = createContext(defaultContext);
|
||||||
|
|
||||||
|
export const ChangeRequestPlausibleProvider =
|
||||||
|
ChangeRequestPlausibleContext.Provider;
|
||||||
|
|
||||||
|
export const useChangeRequestPlausibleContext = (): typeof defaultContext => {
|
||||||
|
return useContext(ChangeRequestPlausibleContext);
|
||||||
|
};
|
@ -1,4 +1,4 @@
|
|||||||
import { VFC } from 'react';
|
import { useState, VFC } from 'react';
|
||||||
import { Box, Button, styled, Typography } from '@mui/material';
|
import { Box, Button, styled, Typography } from '@mui/material';
|
||||||
import { DynamicSidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
import { DynamicSidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
@ -11,6 +11,7 @@ import useToast from 'hooks/useToast';
|
|||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { EnvironmentChangeRequest } from './EnvironmentChangeRequest/EnvironmentChangeRequest';
|
import { EnvironmentChangeRequest } from './EnvironmentChangeRequest/EnvironmentChangeRequest';
|
||||||
import { ReviewChangesHeader } from './ReviewChangesHeader/ReviewChangesHeader';
|
import { ReviewChangesHeader } from './ReviewChangesHeader/ReviewChangesHeader';
|
||||||
|
import { ChangeRequestPlausibleProvider } from '../ChangeRequestContext';
|
||||||
|
|
||||||
interface IChangeRequestSidebarProps {
|
interface IChangeRequestSidebarProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -76,15 +77,18 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
|
|||||||
loading,
|
loading,
|
||||||
refetch: refetchChangeRequest,
|
refetch: refetchChangeRequest,
|
||||||
} = usePendingChangeRequests(project);
|
} = usePendingChangeRequests(project);
|
||||||
const { changeState, discardDraft } = useChangeRequestApi();
|
const { discardDraft } = useChangeRequestApi();
|
||||||
const { setToastApiError } = useToast();
|
const { setToastApiError } = useToast();
|
||||||
|
const [
|
||||||
|
changeRequestChangesWillOverwrite,
|
||||||
|
setChangeRequestChangesWillOverwrite,
|
||||||
|
] = useState(false);
|
||||||
|
|
||||||
const onReview = async (draftId: number, comment?: string) => {
|
const onReview = async (
|
||||||
|
changeState: (project: string) => Promise<void>,
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
await changeState(project, draftId, 'Draft', {
|
await changeState(project);
|
||||||
state: 'In review',
|
|
||||||
comment,
|
|
||||||
});
|
|
||||||
refetchChangeRequest();
|
refetchChangeRequest();
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
setToastApiError(formatUnknownError(error));
|
setToastApiError(formatUnknownError(error));
|
||||||
@ -130,19 +134,29 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
|
|||||||
header={<ReviewChangesHeader />}
|
header={<ReviewChangesHeader />}
|
||||||
>
|
>
|
||||||
{data?.map((environmentChangeRequest) => (
|
{data?.map((environmentChangeRequest) => (
|
||||||
<EnvironmentChangeRequest
|
<ChangeRequestPlausibleProvider
|
||||||
key={environmentChangeRequest.id}
|
key={environmentChangeRequest.id}
|
||||||
environmentChangeRequest={environmentChangeRequest}
|
value={{
|
||||||
onClose={onClose}
|
willOverwriteStrategyChanges:
|
||||||
onReview={onReview}
|
changeRequestChangesWillOverwrite,
|
||||||
onDiscard={onDiscard}
|
registerWillOverwriteStrategyChanges: () =>
|
||||||
|
setChangeRequestChangesWillOverwrite(true),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ChangeRequest
|
<EnvironmentChangeRequest
|
||||||
changeRequest={environmentChangeRequest}
|
key={environmentChangeRequest.id}
|
||||||
onNavigate={onClose}
|
environmentChangeRequest={environmentChangeRequest}
|
||||||
onRefetch={refetchChangeRequest}
|
onClose={onClose}
|
||||||
/>
|
onReview={onReview}
|
||||||
</EnvironmentChangeRequest>
|
onDiscard={onDiscard}
|
||||||
|
>
|
||||||
|
<ChangeRequest
|
||||||
|
changeRequest={environmentChangeRequest}
|
||||||
|
onNavigate={onClose}
|
||||||
|
onRefetch={refetchChangeRequest}
|
||||||
|
/>
|
||||||
|
</EnvironmentChangeRequest>
|
||||||
|
</ChangeRequestPlausibleProvider>
|
||||||
))}
|
))}
|
||||||
</StyledPageContent>
|
</StyledPageContent>
|
||||||
</DynamicSidebarModal>
|
</DynamicSidebarModal>
|
||||||
|
@ -23,6 +23,7 @@ import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
|||||||
import Input from 'component/common/Input/Input';
|
import Input from 'component/common/Input/Input';
|
||||||
import { ChangeRequestTitle } from './ChangeRequestTitle';
|
import { ChangeRequestTitle } from './ChangeRequestTitle';
|
||||||
import { UpdateCount } from 'component/changeRequest/UpdateCount';
|
import { UpdateCount } from 'component/changeRequest/UpdateCount';
|
||||||
|
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
||||||
|
|
||||||
const SubmitChangeRequestButton: FC<{ onClick: () => void; count: number }> = ({
|
const SubmitChangeRequestButton: FC<{ onClick: () => void; count: number }> = ({
|
||||||
onClick,
|
onClick,
|
||||||
@ -56,7 +57,7 @@ const ChangeRequestContent = styled(Box)(({ theme }) => ({
|
|||||||
export const EnvironmentChangeRequest: FC<{
|
export const EnvironmentChangeRequest: FC<{
|
||||||
environmentChangeRequest: ChangeRequestType;
|
environmentChangeRequest: ChangeRequestType;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onReview: (id: number, comment?: string) => void;
|
onReview: (changeState: (project: string) => Promise<void>) => void;
|
||||||
onDiscard: (id: number) => void;
|
onDiscard: (id: number) => void;
|
||||||
}> = ({ environmentChangeRequest, onClose, onReview, onDiscard, children }) => {
|
}> = ({ environmentChangeRequest, onClose, onReview, onDiscard, children }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -64,6 +65,12 @@ export const EnvironmentChangeRequest: FC<{
|
|||||||
const [commentText, setCommentText] = useState('');
|
const [commentText, setCommentText] = useState('');
|
||||||
const { user } = useAuthUser();
|
const { user } = useAuthUser();
|
||||||
const [title, setTitle] = useState(environmentChangeRequest.title);
|
const [title, setTitle] = useState(environmentChangeRequest.title);
|
||||||
|
const { changeState } = useChangeRequestApi();
|
||||||
|
const sendToReview = async (project: string) =>
|
||||||
|
changeState(project, environmentChangeRequest.id, 'Draft', {
|
||||||
|
state: 'In review',
|
||||||
|
comment: commentText,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box key={environmentChangeRequest.id}>
|
<Box key={environmentChangeRequest.id}>
|
||||||
@ -141,12 +148,7 @@ export const EnvironmentChangeRequest: FC<{
|
|||||||
show={
|
show={
|
||||||
<>
|
<>
|
||||||
<SubmitChangeRequestButton
|
<SubmitChangeRequestButton
|
||||||
onClick={() =>
|
onClick={() => onReview(sendToReview)}
|
||||||
onReview(
|
|
||||||
environmentChangeRequest.id,
|
|
||||||
commentText,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
count={changesCount(
|
count={changesCount(
|
||||||
environmentChangeRequest,
|
environmentChangeRequest,
|
||||||
)}
|
)}
|
||||||
|
@ -41,6 +41,7 @@ import { Badge } from 'component/common/Badge/Badge';
|
|||||||
import { ProjectDoraMetrics } from './ProjectDoraMetrics/ProjectDoraMetrics';
|
import { ProjectDoraMetrics } from './ProjectDoraMetrics/ProjectDoraMetrics';
|
||||||
import { UiFlags } from 'interfaces/uiConfig';
|
import { UiFlags } from 'interfaces/uiConfig';
|
||||||
import { HiddenProjectIconWithTooltip } from './HiddenProjectIconWithTooltip/HiddenProjectIconWithTooltip';
|
import { HiddenProjectIconWithTooltip } from './HiddenProjectIconWithTooltip/HiddenProjectIconWithTooltip';
|
||||||
|
import { ChangeRequestPlausibleProvider } from 'component/changeRequest/ChangeRequestContext';
|
||||||
|
|
||||||
const StyledBadge = styled(Badge)(({ theme }) => ({
|
const StyledBadge = styled(Badge)(({ theme }) => ({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -76,6 +77,11 @@ export const Project = () => {
|
|||||||
|
|
||||||
const [showDelDialog, setShowDelDialog] = useState(false);
|
const [showDelDialog, setShowDelDialog] = useState(false);
|
||||||
|
|
||||||
|
const [
|
||||||
|
changeRequestChangesWillOverwrite,
|
||||||
|
setChangeRequestChangesWillOverwrite,
|
||||||
|
] = useState(false);
|
||||||
|
|
||||||
const tabs: ITab[] = [
|
const tabs: ITab[] = [
|
||||||
{
|
{
|
||||||
title: 'Overview',
|
title: 'Overview',
|
||||||
@ -293,7 +299,18 @@ export const Project = () => {
|
|||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path='change-requests/:id'
|
path='change-requests/:id'
|
||||||
element={<ChangeRequestOverview />}
|
element={
|
||||||
|
<ChangeRequestPlausibleProvider
|
||||||
|
value={{
|
||||||
|
willOverwriteStrategyChanges:
|
||||||
|
changeRequestChangesWillOverwrite,
|
||||||
|
registerWillOverwriteStrategyChanges: () =>
|
||||||
|
setChangeRequestChangesWillOverwrite(true),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ChangeRequestOverview />
|
||||||
|
</ChangeRequestPlausibleProvider>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Route path='settings/*' element={<ProjectSettings />} />
|
<Route path='settings/*' element={<ProjectSettings />} />
|
||||||
<Route path='metrics' element={<ProjectDoraMetrics />} />
|
<Route path='metrics' element={<ProjectDoraMetrics />} />
|
||||||
|
@ -3,6 +3,7 @@ import { usePlausibleTracker } from '../../../usePlausibleTracker';
|
|||||||
import { PlausibleChangeRequestState } from 'component/changeRequest/changeRequest.types';
|
import { PlausibleChangeRequestState } from 'component/changeRequest/changeRequest.types';
|
||||||
import { getUniqueChangeRequestId } from 'utils/unique-change-request-id';
|
import { getUniqueChangeRequestId } from 'utils/unique-change-request-id';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import { useChangeRequestPlausibleContext } from 'component/changeRequest/ChangeRequestContext';
|
||||||
|
|
||||||
export interface IChangeSchema {
|
export interface IChangeSchema {
|
||||||
feature: string | null;
|
feature: string | null;
|
||||||
@ -33,6 +34,7 @@ export const useChangeRequestApi = () => {
|
|||||||
propagateErrors: true,
|
propagateErrors: true,
|
||||||
});
|
});
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
|
const { willOverwriteStrategyChanges } = useChangeRequestPlausibleContext();
|
||||||
|
|
||||||
const addChange = async (
|
const addChange = async (
|
||||||
project: string,
|
project: string,
|
||||||
@ -75,6 +77,7 @@ export const useChangeRequestApi = () => {
|
|||||||
props: {
|
props: {
|
||||||
eventType: payload.state,
|
eventType: payload.state,
|
||||||
previousState,
|
previousState,
|
||||||
|
willOverwriteStrategyChanges,
|
||||||
id: getUniqueChangeRequestId(uiConfig, changeRequestId),
|
id: getUniqueChangeRequestId(uiConfig, changeRequestId),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user