mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01: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 { IChangeRequestUpdateStrategy } from 'component/changeRequest/changeRequest.types';
 | 
			
		||||
import { useChangeRequestPlausibleContext } from 'component/changeRequest/ChangeRequestContext';
 | 
			
		||||
import { useUiFlag } from 'hooks/useUiFlag';
 | 
			
		||||
import { IFeatureStrategy } from 'interfaces/strategy';
 | 
			
		||||
import { getChangesThatWouldBeOverwritten } from './strategy-change-diff-calculation';
 | 
			
		||||
import { useEffect } from 'react';
 | 
			
		||||
 | 
			
		||||
const ChangesToOverwriteWarning = styled(Box)(({ theme }) => ({
 | 
			
		||||
    color: theme.palette.warning.dark,
 | 
			
		||||
@ -76,6 +78,14 @@ export const ChangesToOverwrite: React.FC<{
 | 
			
		||||
    const changesThatWouldBeOverwritten = checkForChanges
 | 
			
		||||
        ? getChangesThatWouldBeOverwritten(currentStrategy, change)
 | 
			
		||||
        : null;
 | 
			
		||||
    const { registerWillOverwriteStrategyChanges } =
 | 
			
		||||
        useChangeRequestPlausibleContext();
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (changesThatWouldBeOverwritten) {
 | 
			
		||||
            registerWillOverwriteStrategyChanges();
 | 
			
		||||
        }
 | 
			
		||||
    }, [changesThatWouldBeOverwritten]);
 | 
			
		||||
 | 
			
		||||
    if (!changesThatWouldBeOverwritten) {
 | 
			
		||||
        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 { DynamicSidebarModal } from 'component/common/SidebarModal/SidebarModal';
 | 
			
		||||
import { PageContent } from 'component/common/PageContent/PageContent';
 | 
			
		||||
@ -11,6 +11,7 @@ import useToast from 'hooks/useToast';
 | 
			
		||||
import { formatUnknownError } from 'utils/formatUnknownError';
 | 
			
		||||
import { EnvironmentChangeRequest } from './EnvironmentChangeRequest/EnvironmentChangeRequest';
 | 
			
		||||
import { ReviewChangesHeader } from './ReviewChangesHeader/ReviewChangesHeader';
 | 
			
		||||
import { ChangeRequestPlausibleProvider } from '../ChangeRequestContext';
 | 
			
		||||
 | 
			
		||||
interface IChangeRequestSidebarProps {
 | 
			
		||||
    open: boolean;
 | 
			
		||||
@ -76,15 +77,18 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
 | 
			
		||||
        loading,
 | 
			
		||||
        refetch: refetchChangeRequest,
 | 
			
		||||
    } = usePendingChangeRequests(project);
 | 
			
		||||
    const { changeState, discardDraft } = useChangeRequestApi();
 | 
			
		||||
    const { discardDraft } = useChangeRequestApi();
 | 
			
		||||
    const { setToastApiError } = useToast();
 | 
			
		||||
    const [
 | 
			
		||||
        changeRequestChangesWillOverwrite,
 | 
			
		||||
        setChangeRequestChangesWillOverwrite,
 | 
			
		||||
    ] = useState(false);
 | 
			
		||||
 | 
			
		||||
    const onReview = async (draftId: number, comment?: string) => {
 | 
			
		||||
    const onReview = async (
 | 
			
		||||
        changeState: (project: string) => Promise<void>,
 | 
			
		||||
    ) => {
 | 
			
		||||
        try {
 | 
			
		||||
            await changeState(project, draftId, 'Draft', {
 | 
			
		||||
                state: 'In review',
 | 
			
		||||
                comment,
 | 
			
		||||
            });
 | 
			
		||||
            await changeState(project);
 | 
			
		||||
            refetchChangeRequest();
 | 
			
		||||
        } catch (error: unknown) {
 | 
			
		||||
            setToastApiError(formatUnknownError(error));
 | 
			
		||||
@ -130,19 +134,29 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
 | 
			
		||||
                header={<ReviewChangesHeader />}
 | 
			
		||||
            >
 | 
			
		||||
                {data?.map((environmentChangeRequest) => (
 | 
			
		||||
                    <EnvironmentChangeRequest
 | 
			
		||||
                    <ChangeRequestPlausibleProvider
 | 
			
		||||
                        key={environmentChangeRequest.id}
 | 
			
		||||
                        environmentChangeRequest={environmentChangeRequest}
 | 
			
		||||
                        onClose={onClose}
 | 
			
		||||
                        onReview={onReview}
 | 
			
		||||
                        onDiscard={onDiscard}
 | 
			
		||||
                        value={{
 | 
			
		||||
                            willOverwriteStrategyChanges:
 | 
			
		||||
                                changeRequestChangesWillOverwrite,
 | 
			
		||||
                            registerWillOverwriteStrategyChanges: () =>
 | 
			
		||||
                                setChangeRequestChangesWillOverwrite(true),
 | 
			
		||||
                        }}
 | 
			
		||||
                    >
 | 
			
		||||
                        <ChangeRequest
 | 
			
		||||
                            changeRequest={environmentChangeRequest}
 | 
			
		||||
                            onNavigate={onClose}
 | 
			
		||||
                            onRefetch={refetchChangeRequest}
 | 
			
		||||
                        />
 | 
			
		||||
                    </EnvironmentChangeRequest>
 | 
			
		||||
                        <EnvironmentChangeRequest
 | 
			
		||||
                            key={environmentChangeRequest.id}
 | 
			
		||||
                            environmentChangeRequest={environmentChangeRequest}
 | 
			
		||||
                            onClose={onClose}
 | 
			
		||||
                            onReview={onReview}
 | 
			
		||||
                            onDiscard={onDiscard}
 | 
			
		||||
                        >
 | 
			
		||||
                            <ChangeRequest
 | 
			
		||||
                                changeRequest={environmentChangeRequest}
 | 
			
		||||
                                onNavigate={onClose}
 | 
			
		||||
                                onRefetch={refetchChangeRequest}
 | 
			
		||||
                            />
 | 
			
		||||
                        </EnvironmentChangeRequest>
 | 
			
		||||
                    </ChangeRequestPlausibleProvider>
 | 
			
		||||
                ))}
 | 
			
		||||
            </StyledPageContent>
 | 
			
		||||
        </DynamicSidebarModal>
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
 | 
			
		||||
import Input from 'component/common/Input/Input';
 | 
			
		||||
import { ChangeRequestTitle } from './ChangeRequestTitle';
 | 
			
		||||
import { UpdateCount } from 'component/changeRequest/UpdateCount';
 | 
			
		||||
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
 | 
			
		||||
 | 
			
		||||
const SubmitChangeRequestButton: FC<{ onClick: () => void; count: number }> = ({
 | 
			
		||||
    onClick,
 | 
			
		||||
@ -56,7 +57,7 @@ const ChangeRequestContent = styled(Box)(({ theme }) => ({
 | 
			
		||||
export const EnvironmentChangeRequest: FC<{
 | 
			
		||||
    environmentChangeRequest: ChangeRequestType;
 | 
			
		||||
    onClose: () => void;
 | 
			
		||||
    onReview: (id: number, comment?: string) => void;
 | 
			
		||||
    onReview: (changeState: (project: string) => Promise<void>) => void;
 | 
			
		||||
    onDiscard: (id: number) => void;
 | 
			
		||||
}> = ({ environmentChangeRequest, onClose, onReview, onDiscard, children }) => {
 | 
			
		||||
    const theme = useTheme();
 | 
			
		||||
@ -64,6 +65,12 @@ export const EnvironmentChangeRequest: FC<{
 | 
			
		||||
    const [commentText, setCommentText] = useState('');
 | 
			
		||||
    const { user } = useAuthUser();
 | 
			
		||||
    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 (
 | 
			
		||||
        <Box key={environmentChangeRequest.id}>
 | 
			
		||||
@ -141,12 +148,7 @@ export const EnvironmentChangeRequest: FC<{
 | 
			
		||||
                        show={
 | 
			
		||||
                            <>
 | 
			
		||||
                                <SubmitChangeRequestButton
 | 
			
		||||
                                    onClick={() =>
 | 
			
		||||
                                        onReview(
 | 
			
		||||
                                            environmentChangeRequest.id,
 | 
			
		||||
                                            commentText,
 | 
			
		||||
                                        )
 | 
			
		||||
                                    }
 | 
			
		||||
                                    onClick={() => onReview(sendToReview)}
 | 
			
		||||
                                    count={changesCount(
 | 
			
		||||
                                        environmentChangeRequest,
 | 
			
		||||
                                    )}
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,7 @@ import { Badge } from 'component/common/Badge/Badge';
 | 
			
		||||
import { ProjectDoraMetrics } from './ProjectDoraMetrics/ProjectDoraMetrics';
 | 
			
		||||
import { UiFlags } from 'interfaces/uiConfig';
 | 
			
		||||
import { HiddenProjectIconWithTooltip } from './HiddenProjectIconWithTooltip/HiddenProjectIconWithTooltip';
 | 
			
		||||
import { ChangeRequestPlausibleProvider } from 'component/changeRequest/ChangeRequestContext';
 | 
			
		||||
 | 
			
		||||
const StyledBadge = styled(Badge)(({ theme }) => ({
 | 
			
		||||
    position: 'absolute',
 | 
			
		||||
@ -76,6 +77,11 @@ export const Project = () => {
 | 
			
		||||
 | 
			
		||||
    const [showDelDialog, setShowDelDialog] = useState(false);
 | 
			
		||||
 | 
			
		||||
    const [
 | 
			
		||||
        changeRequestChangesWillOverwrite,
 | 
			
		||||
        setChangeRequestChangesWillOverwrite,
 | 
			
		||||
    ] = useState(false);
 | 
			
		||||
 | 
			
		||||
    const tabs: ITab[] = [
 | 
			
		||||
        {
 | 
			
		||||
            title: 'Overview',
 | 
			
		||||
@ -293,7 +299,18 @@ export const Project = () => {
 | 
			
		||||
                />
 | 
			
		||||
                <Route
 | 
			
		||||
                    path='change-requests/:id'
 | 
			
		||||
                    element={<ChangeRequestOverview />}
 | 
			
		||||
                    element={
 | 
			
		||||
                        <ChangeRequestPlausibleProvider
 | 
			
		||||
                            value={{
 | 
			
		||||
                                willOverwriteStrategyChanges:
 | 
			
		||||
                                    changeRequestChangesWillOverwrite,
 | 
			
		||||
                                registerWillOverwriteStrategyChanges: () =>
 | 
			
		||||
                                    setChangeRequestChangesWillOverwrite(true),
 | 
			
		||||
                            }}
 | 
			
		||||
                        >
 | 
			
		||||
                            <ChangeRequestOverview />
 | 
			
		||||
                        </ChangeRequestPlausibleProvider>
 | 
			
		||||
                    }
 | 
			
		||||
                />
 | 
			
		||||
                <Route path='settings/*' element={<ProjectSettings />} />
 | 
			
		||||
                <Route path='metrics' element={<ProjectDoraMetrics />} />
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ import { usePlausibleTracker } from '../../../usePlausibleTracker';
 | 
			
		||||
import { PlausibleChangeRequestState } from 'component/changeRequest/changeRequest.types';
 | 
			
		||||
import { getUniqueChangeRequestId } from 'utils/unique-change-request-id';
 | 
			
		||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
			
		||||
import { useChangeRequestPlausibleContext } from 'component/changeRequest/ChangeRequestContext';
 | 
			
		||||
 | 
			
		||||
export interface IChangeSchema {
 | 
			
		||||
    feature: string | null;
 | 
			
		||||
@ -33,6 +34,7 @@ export const useChangeRequestApi = () => {
 | 
			
		||||
        propagateErrors: true,
 | 
			
		||||
    });
 | 
			
		||||
    const { uiConfig } = useUiConfig();
 | 
			
		||||
    const { willOverwriteStrategyChanges } = useChangeRequestPlausibleContext();
 | 
			
		||||
 | 
			
		||||
    const addChange = async (
 | 
			
		||||
        project: string,
 | 
			
		||||
@ -75,6 +77,7 @@ export const useChangeRequestApi = () => {
 | 
			
		||||
            props: {
 | 
			
		||||
                eventType: payload.state,
 | 
			
		||||
                previousState,
 | 
			
		||||
                willOverwriteStrategyChanges,
 | 
			
		||||
                id: getUniqueChangeRequestId(uiConfig, changeRequestId),
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user