From fab6fbb756ac019fef89f29cf9367c88d0fc1f99 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 30 Nov 2022 12:04:29 +0100 Subject: [PATCH] Change request event tracking (#2570) --- .../src/assets/img/changeRequestProcess.svg | 122 ++++++------------ .../ChangeRequestOverview.tsx | 2 +- .../FeatureStrategyCreate.tsx | 4 +- .../FeatureStrategyEdit.tsx | 4 +- .../FeatureStrategyRemove.tsx | 4 +- .../ChangeRequestConfiguration.tsx | 14 +- .../ChangeRequestProcessHelp.tsx | 18 ++- .../useChangeRequestApi.ts | 30 ++++- .../src/hooks/useChangeRequestAddStrategy.ts | 22 ++-- frontend/src/hooks/useChangeRequestToggle.ts | 22 ++-- frontend/src/hooks/usePlausibleTracker.ts | 2 +- 11 files changed, 115 insertions(+), 129 deletions(-) diff --git a/frontend/src/assets/img/changeRequestProcess.svg b/frontend/src/assets/img/changeRequestProcess.svg index 6e8c755fd0..61b71d5473 100644 --- a/frontend/src/assets/img/changeRequestProcess.svg +++ b/frontend/src/assets/img/changeRequestProcess.svg @@ -1,90 +1,48 @@ - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx index acc591b6d7..e3d626e699 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx @@ -1,4 +1,4 @@ -import { Alert, Button, styled } from '@mui/material'; +import { Alert, Button, styled, Typography } from '@mui/material'; import { FC, useContext, useState } from 'react'; import { Box } from '@mui/material'; import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx index 69c068543c..b6fac67c90 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx @@ -41,7 +41,7 @@ export const FeatureStrategyCreate = () => { const errors = useFormErrors(); const { addStrategyToFeature, loading } = useFeatureStrategyApi(); - const { addChangeRequest } = useChangeRequestApi(); + const { addChange } = useChangeRequestApi(); const { setToastData, setToastApiError } = useToast(); const { uiConfig } = useUiConfig(); const { unleashUrl } = uiConfig; @@ -98,7 +98,7 @@ export const FeatureStrategyCreate = () => { }; const onStrategyRequestAdd = async (payload: IFeatureStrategyPayload) => { - await addChangeRequest(projectId, environmentId, { + await addChange(projectId, environmentId, { action: 'addStrategy', feature: featureId, payload, diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx index 4a1cef04c6..3af146b03a 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx @@ -43,7 +43,7 @@ export const FeatureStrategyEdit = () => { const { uiConfig } = useUiConfig(); const { unleashUrl } = uiConfig; const navigate = useNavigate(); - const { addChangeRequest } = useChangeRequestApi(); + const { addChange } = useChangeRequestApi(); const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId); const { refetch: refetchChangeRequests } = usePendingChangeRequests(projectId); @@ -110,7 +110,7 @@ export const FeatureStrategyEdit = () => { }; const onStrategyRequestEdit = async (payload: IFeatureStrategyPayload) => { - await addChangeRequest(projectId, environmentId, { + await addChange(projectId, environmentId, { action: 'updateStrategy', feature: featureId, payload: { ...payload, id: strategyId }, diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyRemove/FeatureStrategyRemove.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyRemove/FeatureStrategyRemove.tsx index 59a979a89d..4036507eb9 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyRemove/FeatureStrategyRemove.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyRemove/FeatureStrategyRemove.tsx @@ -130,14 +130,14 @@ const useOnSuggestRemove = ({ environmentId, strategyId, }: IRemoveProps) => { - const { addChangeRequest } = useChangeRequestApi(); + const { addChange } = useChangeRequestApi(); const { refetch: refetchChangeRequests } = usePendingChangeRequests(projectId); const { setToastData, setToastApiError } = useToast(); const onSuggestRemove = async (event: React.FormEvent) => { try { event.preventDefault(); - await addChangeRequest(projectId, environmentId, { + await addChange(projectId, environmentId, { action: 'deleteStrategy', feature: featureId, payload: { diff --git a/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestConfiguration.tsx b/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestConfiguration.tsx index 0f548cdd21..a3b249da28 100644 --- a/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestConfiguration.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestConfiguration.tsx @@ -29,6 +29,7 @@ import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect'; import { KeyboardArrowDownOutlined } from '@mui/icons-material'; import { useTheme } from '@mui/material/styles'; import AccessContext from 'contexts/AccessContext'; +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; const StyledBox = styled(Box)(({ theme }) => ({ padding: theme.spacing(1), @@ -40,6 +41,7 @@ const StyledBox = styled(Box)(({ theme }) => ({ })); export const ChangeRequestConfiguration: VFC = () => { + const { trackEvent } = usePlausibleTracker(); const [dialogState, setDialogState] = useState<{ isOpen: boolean; enableEnvironment: string; @@ -256,7 +258,17 @@ export const ChangeRequestConfiguration: VFC = () => { onConfirm()} + onClick={() => { + trackEvent('change_request', { + props: { + eventType: `change request ${ + !dialogState.isEnabled ? 'enabled' : 'disabled' + }`, + }, + }); + + onConfirm(); + }} open={dialogState.isOpen} onClose={() => setDialogState(state => ({ ...state, isOpen: false })) diff --git a/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestProcessHelp/ChangeRequestProcessHelp.tsx b/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestProcessHelp/ChangeRequestProcessHelp.tsx index 70e5ce1fff..dae32e8301 100644 --- a/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestProcessHelp/ChangeRequestProcessHelp.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/ChangeRequestConfiguration/ChangeRequestProcessHelp/ChangeRequestProcessHelp.tsx @@ -31,8 +31,12 @@ export const ChangeRequestProcessHelp: VFC< } /> - - setIsOpen(true)} /> + setIsOpen(true)} + > + These changes can be seen by everyone but only who has “Review change request”{' '} - permission can Approve or Reject them + permission can Approve them
  • - If changes are Approved then someone who has + If changes are Approved then someone who has{' '} “Apply change request”{' '} permission needs to apply these changes to be live on the feature toggles and request is Closed
  • - If changes are Rejected then these goes - automatically to Cancelled and request is - Closed. + If changes are Cancelled by the author or + admin then change request goes automatically + to Cancelled and request is Closed.
diff --git a/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts b/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts index 0589065d34..5e926104d2 100644 --- a/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts +++ b/frontend/src/hooks/api/actions/useChangeRequestApi/useChangeRequestApi.ts @@ -1,6 +1,7 @@ import useAPI from '../useApi/useApi'; +import { usePlausibleTracker } from '../../../usePlausibleTracker'; -export interface IChangeRequestsSchema { +export interface IChangeSchema { feature: string; action: | 'updateEnabled' @@ -18,15 +19,22 @@ export interface IChangeRequestConfig { } export const useChangeRequestApi = () => { + const { trackEvent } = usePlausibleTracker(); const { makeRequest, createRequest, errors, loading } = useAPI({ propagateErrors: true, }); - const addChangeRequest = async ( + const addChange = async ( project: string, environment: string, - payload: IChangeRequestsSchema + payload: IChangeSchema ) => { + trackEvent('change_request', { + props: { + eventType: 'change added', + }, + }); + const path = `api/admin/projects/${project}/environments/${environment}/change-requests`; const req = createRequest(path, { method: 'POST', @@ -43,8 +51,14 @@ export const useChangeRequestApi = () => { const changeState = async ( project: string, changeRequestId: number, - payload: any + payload: { state: 'Approved' | 'Applied' | 'Cancelled' | 'In review' } ) => { + trackEvent('change_request', { + props: { + eventType: payload.state, + }, + }); + const path = `api/admin/projects/${project}/change-requests/${changeRequestId}/state`; const req = createRequest(path, { method: 'PUT', @@ -114,6 +128,12 @@ export const useChangeRequestApi = () => { changeRequestId: string, text: string ) => { + trackEvent('change_request', { + props: { + eventType: 'comment added', + }, + }); + const path = `/api/admin/projects/${projectId}/change-requests/${changeRequestId}/comments`; const req = createRequest(path, { method: 'POST', @@ -128,7 +148,7 @@ export const useChangeRequestApi = () => { }; return { - addChangeRequest, + addChange, changeState, discardChange, updateChangeRequestEnvironmentConfig, diff --git a/frontend/src/hooks/useChangeRequestAddStrategy.ts b/frontend/src/hooks/useChangeRequestAddStrategy.ts index d9e15c0e1f..beb068ab39 100644 --- a/frontend/src/hooks/useChangeRequestAddStrategy.ts +++ b/frontend/src/hooks/useChangeRequestAddStrategy.ts @@ -16,7 +16,7 @@ export const useChangeRequestAddStrategy = ( action: ChangeRequestStrategyAction ) => { const { setToastData, setToastApiError } = useToast(); - const { addChangeRequest } = useChangeRequestApi(); + const { addChange } = useChangeRequestApi(); const { refetch } = usePendingChangeRequests(project); const [changeRequestDialogDetails, setChangeRequestDialogDetails] = @@ -69,15 +69,11 @@ export const useChangeRequestAddStrategy = ( const onChangeRequestAddStrategyConfirm = useCallback(async () => { try { - await addChangeRequest( - project, - changeRequestDialogDetails.environment!, - { - feature: changeRequestDialogDetails.featureName!, - action: action, - payload: changeRequestDialogDetails.strategy!, - } - ); + await addChange(project, changeRequestDialogDetails.environment!, { + feature: changeRequestDialogDetails.featureName!, + action: action, + payload: changeRequestDialogDetails.strategy!, + }); refetch(); setChangeRequestDialogDetails({ isOpen: false }); setToastData({ @@ -88,13 +84,13 @@ export const useChangeRequestAddStrategy = ( setToastApiError(formatUnknownError(error)); setChangeRequestDialogDetails({ isOpen: false }); } - }, [addChangeRequest]); + }, [addChange]); const onChangeRequestAddStrategiesConfirm = useCallback(async () => { try { await Promise.all( changeRequestDialogDetails.strategies!.map(strategy => { - return addChangeRequest( + return addChange( project, changeRequestDialogDetails.environment!, { @@ -115,7 +111,7 @@ export const useChangeRequestAddStrategy = ( setToastApiError(formatUnknownError(error)); setChangeRequestDialogDetails({ isOpen: false }); } - }, [addChangeRequest]); + }, [addChange]); return { onChangeRequestAddStrategy, diff --git a/frontend/src/hooks/useChangeRequestToggle.ts b/frontend/src/hooks/useChangeRequestToggle.ts index f7fffdeebb..863fbf3a08 100644 --- a/frontend/src/hooks/useChangeRequestToggle.ts +++ b/frontend/src/hooks/useChangeRequestToggle.ts @@ -6,7 +6,7 @@ import { usePendingChangeRequests } from './api/getters/usePendingChangeRequests export const useChangeRequestToggle = (project: string) => { const { setToastData, setToastApiError } = useToast(); - const { addChangeRequest } = useChangeRequestApi(); + const { addChange } = useChangeRequestApi(); const { refetch: refetchChangeRequests } = usePendingChangeRequests(project); @@ -36,17 +36,13 @@ export const useChangeRequestToggle = (project: string) => { const onChangeRequestToggleConfirm = useCallback(async () => { try { - await addChangeRequest( - project, - changeRequestDialogDetails.environment!, - { - feature: changeRequestDialogDetails.featureName!, - action: 'updateEnabled', - payload: { - enabled: Boolean(changeRequestDialogDetails.enabled), - }, - } - ); + await addChange(project, changeRequestDialogDetails.environment!, { + feature: changeRequestDialogDetails.featureName!, + action: 'updateEnabled', + payload: { + enabled: Boolean(changeRequestDialogDetails.enabled), + }, + }); refetchChangeRequests(); setChangeRequestDialogDetails(prev => ({ ...prev, isOpen: false })); setToastData({ @@ -57,7 +53,7 @@ export const useChangeRequestToggle = (project: string) => { setToastApiError(formatUnknownError(error)); setChangeRequestDialogDetails(prev => ({ ...prev, isOpen: false })); } - }, [addChangeRequest]); + }, [addChange]); return { onChangeRequestToggle, diff --git a/frontend/src/hooks/usePlausibleTracker.ts b/frontend/src/hooks/usePlausibleTracker.ts index 4bce09b4a3..adda401e84 100644 --- a/frontend/src/hooks/usePlausibleTracker.ts +++ b/frontend/src/hooks/usePlausibleTracker.ts @@ -8,7 +8,7 @@ import { EventOptions, PlausibleOptions } from 'plausible-tracker'; * @see https://plausible.io/docs/custom-event-goals#2-create-a-custom-event-goal-in-your-plausible-analytics-account * @example `'download | 'invite' | 'signup'` **/ -type CustomEvents = 'invite' | 'upgrade_plan_clicked'; +type CustomEvents = 'invite' | 'upgrade_plan_clicked' | 'change_request'; export const usePlausibleTracker = () => { const plausible = useContext(PlausibleContext);