mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-26 01:17:00 +02:00
Change request event tracking (#2570)
This commit is contained in:
parent
d1c565735a
commit
fab6fbb756
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 194 KiB |
@ -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 { FC, useContext, useState } from 'react';
|
||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
|
import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest';
|
||||||
|
@ -41,7 +41,7 @@ export const FeatureStrategyCreate = () => {
|
|||||||
const errors = useFormErrors();
|
const errors = useFormErrors();
|
||||||
|
|
||||||
const { addStrategyToFeature, loading } = useFeatureStrategyApi();
|
const { addStrategyToFeature, loading } = useFeatureStrategyApi();
|
||||||
const { addChangeRequest } = useChangeRequestApi();
|
const { addChange } = useChangeRequestApi();
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const { unleashUrl } = uiConfig;
|
const { unleashUrl } = uiConfig;
|
||||||
@ -98,7 +98,7 @@ export const FeatureStrategyCreate = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onStrategyRequestAdd = async (payload: IFeatureStrategyPayload) => {
|
const onStrategyRequestAdd = async (payload: IFeatureStrategyPayload) => {
|
||||||
await addChangeRequest(projectId, environmentId, {
|
await addChange(projectId, environmentId, {
|
||||||
action: 'addStrategy',
|
action: 'addStrategy',
|
||||||
feature: featureId,
|
feature: featureId,
|
||||||
payload,
|
payload,
|
||||||
|
@ -43,7 +43,7 @@ export const FeatureStrategyEdit = () => {
|
|||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const { unleashUrl } = uiConfig;
|
const { unleashUrl } = uiConfig;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { addChangeRequest } = useChangeRequestApi();
|
const { addChange } = useChangeRequestApi();
|
||||||
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
|
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
|
||||||
const { refetch: refetchChangeRequests } =
|
const { refetch: refetchChangeRequests } =
|
||||||
usePendingChangeRequests(projectId);
|
usePendingChangeRequests(projectId);
|
||||||
@ -110,7 +110,7 @@ export const FeatureStrategyEdit = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onStrategyRequestEdit = async (payload: IFeatureStrategyPayload) => {
|
const onStrategyRequestEdit = async (payload: IFeatureStrategyPayload) => {
|
||||||
await addChangeRequest(projectId, environmentId, {
|
await addChange(projectId, environmentId, {
|
||||||
action: 'updateStrategy',
|
action: 'updateStrategy',
|
||||||
feature: featureId,
|
feature: featureId,
|
||||||
payload: { ...payload, id: strategyId },
|
payload: { ...payload, id: strategyId },
|
||||||
|
@ -130,14 +130,14 @@ const useOnSuggestRemove = ({
|
|||||||
environmentId,
|
environmentId,
|
||||||
strategyId,
|
strategyId,
|
||||||
}: IRemoveProps) => {
|
}: IRemoveProps) => {
|
||||||
const { addChangeRequest } = useChangeRequestApi();
|
const { addChange } = useChangeRequestApi();
|
||||||
const { refetch: refetchChangeRequests } =
|
const { refetch: refetchChangeRequests } =
|
||||||
usePendingChangeRequests(projectId);
|
usePendingChangeRequests(projectId);
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const onSuggestRemove = async (event: React.FormEvent) => {
|
const onSuggestRemove = async (event: React.FormEvent) => {
|
||||||
try {
|
try {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
await addChangeRequest(projectId, environmentId, {
|
await addChange(projectId, environmentId, {
|
||||||
action: 'deleteStrategy',
|
action: 'deleteStrategy',
|
||||||
feature: featureId,
|
feature: featureId,
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -29,6 +29,7 @@ import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
|
|||||||
import { KeyboardArrowDownOutlined } from '@mui/icons-material';
|
import { KeyboardArrowDownOutlined } from '@mui/icons-material';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import AccessContext from 'contexts/AccessContext';
|
import AccessContext from 'contexts/AccessContext';
|
||||||
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
|
|
||||||
const StyledBox = styled(Box)(({ theme }) => ({
|
const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
padding: theme.spacing(1),
|
padding: theme.spacing(1),
|
||||||
@ -40,6 +41,7 @@ const StyledBox = styled(Box)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const ChangeRequestConfiguration: VFC = () => {
|
export const ChangeRequestConfiguration: VFC = () => {
|
||||||
|
const { trackEvent } = usePlausibleTracker();
|
||||||
const [dialogState, setDialogState] = useState<{
|
const [dialogState, setDialogState] = useState<{
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
enableEnvironment: string;
|
enableEnvironment: string;
|
||||||
@ -256,7 +258,17 @@ export const ChangeRequestConfiguration: VFC = () => {
|
|||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<Dialogue
|
<Dialogue
|
||||||
onClick={() => onConfirm()}
|
onClick={() => {
|
||||||
|
trackEvent('change_request', {
|
||||||
|
props: {
|
||||||
|
eventType: `change request ${
|
||||||
|
!dialogState.isEnabled ? 'enabled' : 'disabled'
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onConfirm();
|
||||||
|
}}
|
||||||
open={dialogState.isOpen}
|
open={dialogState.isOpen}
|
||||||
onClose={() =>
|
onClose={() =>
|
||||||
setDialogState(state => ({ ...state, isOpen: false }))
|
setDialogState(state => ({ ...state, isOpen: false }))
|
||||||
|
@ -31,8 +31,12 @@ export const ChangeRequestProcessHelp: VFC<
|
|||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IconButton title="Change request process" ref={ref}>
|
<IconButton
|
||||||
<HelpOutline onClick={() => setIsOpen(true)} />
|
title="Change request process"
|
||||||
|
ref={ref}
|
||||||
|
onClick={() => setIsOpen(true)}
|
||||||
|
>
|
||||||
|
<HelpOutline />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Popover
|
<Popover
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
@ -64,20 +68,20 @@ export const ChangeRequestProcessHelp: VFC<
|
|||||||
<li>
|
<li>
|
||||||
These changes can be seen by everyone but only
|
These changes can be seen by everyone but only
|
||||||
who has <strong>“Review change request”</strong>{' '}
|
who has <strong>“Review change request”</strong>{' '}
|
||||||
permission can Approve or Reject them
|
permission can Approve them
|
||||||
</li>
|
</li>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
If changes are Approved then someone who has
|
If changes are Approved then someone who has{' '}
|
||||||
<strong>“Apply change request”</strong>{' '}
|
<strong>“Apply change request”</strong>{' '}
|
||||||
permission needs to apply these changes to
|
permission needs to apply these changes to
|
||||||
be live on the feature toggles and request
|
be live on the feature toggles and request
|
||||||
is Closed
|
is Closed
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
If changes are Rejected then these goes
|
If changes are Cancelled by the author or
|
||||||
automatically to Cancelled and request is
|
admin then change request goes automatically
|
||||||
Closed.
|
to Cancelled and request is Closed.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ol>
|
</ol>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import useAPI from '../useApi/useApi';
|
import useAPI from '../useApi/useApi';
|
||||||
|
import { usePlausibleTracker } from '../../../usePlausibleTracker';
|
||||||
|
|
||||||
export interface IChangeRequestsSchema {
|
export interface IChangeSchema {
|
||||||
feature: string;
|
feature: string;
|
||||||
action:
|
action:
|
||||||
| 'updateEnabled'
|
| 'updateEnabled'
|
||||||
@ -18,15 +19,22 @@ export interface IChangeRequestConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useChangeRequestApi = () => {
|
export const useChangeRequestApi = () => {
|
||||||
|
const { trackEvent } = usePlausibleTracker();
|
||||||
const { makeRequest, createRequest, errors, loading } = useAPI({
|
const { makeRequest, createRequest, errors, loading } = useAPI({
|
||||||
propagateErrors: true,
|
propagateErrors: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const addChangeRequest = async (
|
const addChange = async (
|
||||||
project: string,
|
project: string,
|
||||||
environment: 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 path = `api/admin/projects/${project}/environments/${environment}/change-requests`;
|
||||||
const req = createRequest(path, {
|
const req = createRequest(path, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -43,8 +51,14 @@ export const useChangeRequestApi = () => {
|
|||||||
const changeState = async (
|
const changeState = async (
|
||||||
project: string,
|
project: string,
|
||||||
changeRequestId: number,
|
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 path = `api/admin/projects/${project}/change-requests/${changeRequestId}/state`;
|
||||||
const req = createRequest(path, {
|
const req = createRequest(path, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@ -114,6 +128,12 @@ export const useChangeRequestApi = () => {
|
|||||||
changeRequestId: string,
|
changeRequestId: string,
|
||||||
text: string
|
text: string
|
||||||
) => {
|
) => {
|
||||||
|
trackEvent('change_request', {
|
||||||
|
props: {
|
||||||
|
eventType: 'comment added',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const path = `/api/admin/projects/${projectId}/change-requests/${changeRequestId}/comments`;
|
const path = `/api/admin/projects/${projectId}/change-requests/${changeRequestId}/comments`;
|
||||||
const req = createRequest(path, {
|
const req = createRequest(path, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -128,7 +148,7 @@ export const useChangeRequestApi = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
addChangeRequest,
|
addChange,
|
||||||
changeState,
|
changeState,
|
||||||
discardChange,
|
discardChange,
|
||||||
updateChangeRequestEnvironmentConfig,
|
updateChangeRequestEnvironmentConfig,
|
||||||
|
@ -16,7 +16,7 @@ export const useChangeRequestAddStrategy = (
|
|||||||
action: ChangeRequestStrategyAction
|
action: ChangeRequestStrategyAction
|
||||||
) => {
|
) => {
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { addChangeRequest } = useChangeRequestApi();
|
const { addChange } = useChangeRequestApi();
|
||||||
const { refetch } = usePendingChangeRequests(project);
|
const { refetch } = usePendingChangeRequests(project);
|
||||||
|
|
||||||
const [changeRequestDialogDetails, setChangeRequestDialogDetails] =
|
const [changeRequestDialogDetails, setChangeRequestDialogDetails] =
|
||||||
@ -69,15 +69,11 @@ export const useChangeRequestAddStrategy = (
|
|||||||
|
|
||||||
const onChangeRequestAddStrategyConfirm = useCallback(async () => {
|
const onChangeRequestAddStrategyConfirm = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await addChangeRequest(
|
await addChange(project, changeRequestDialogDetails.environment!, {
|
||||||
project,
|
feature: changeRequestDialogDetails.featureName!,
|
||||||
changeRequestDialogDetails.environment!,
|
action: action,
|
||||||
{
|
payload: changeRequestDialogDetails.strategy!,
|
||||||
feature: changeRequestDialogDetails.featureName!,
|
});
|
||||||
action: action,
|
|
||||||
payload: changeRequestDialogDetails.strategy!,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
refetch();
|
refetch();
|
||||||
setChangeRequestDialogDetails({ isOpen: false });
|
setChangeRequestDialogDetails({ isOpen: false });
|
||||||
setToastData({
|
setToastData({
|
||||||
@ -88,13 +84,13 @@ export const useChangeRequestAddStrategy = (
|
|||||||
setToastApiError(formatUnknownError(error));
|
setToastApiError(formatUnknownError(error));
|
||||||
setChangeRequestDialogDetails({ isOpen: false });
|
setChangeRequestDialogDetails({ isOpen: false });
|
||||||
}
|
}
|
||||||
}, [addChangeRequest]);
|
}, [addChange]);
|
||||||
|
|
||||||
const onChangeRequestAddStrategiesConfirm = useCallback(async () => {
|
const onChangeRequestAddStrategiesConfirm = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
changeRequestDialogDetails.strategies!.map(strategy => {
|
changeRequestDialogDetails.strategies!.map(strategy => {
|
||||||
return addChangeRequest(
|
return addChange(
|
||||||
project,
|
project,
|
||||||
changeRequestDialogDetails.environment!,
|
changeRequestDialogDetails.environment!,
|
||||||
{
|
{
|
||||||
@ -115,7 +111,7 @@ export const useChangeRequestAddStrategy = (
|
|||||||
setToastApiError(formatUnknownError(error));
|
setToastApiError(formatUnknownError(error));
|
||||||
setChangeRequestDialogDetails({ isOpen: false });
|
setChangeRequestDialogDetails({ isOpen: false });
|
||||||
}
|
}
|
||||||
}, [addChangeRequest]);
|
}, [addChange]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onChangeRequestAddStrategy,
|
onChangeRequestAddStrategy,
|
||||||
|
@ -6,7 +6,7 @@ import { usePendingChangeRequests } from './api/getters/usePendingChangeRequests
|
|||||||
|
|
||||||
export const useChangeRequestToggle = (project: string) => {
|
export const useChangeRequestToggle = (project: string) => {
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { addChangeRequest } = useChangeRequestApi();
|
const { addChange } = useChangeRequestApi();
|
||||||
const { refetch: refetchChangeRequests } =
|
const { refetch: refetchChangeRequests } =
|
||||||
usePendingChangeRequests(project);
|
usePendingChangeRequests(project);
|
||||||
|
|
||||||
@ -36,17 +36,13 @@ export const useChangeRequestToggle = (project: string) => {
|
|||||||
|
|
||||||
const onChangeRequestToggleConfirm = useCallback(async () => {
|
const onChangeRequestToggleConfirm = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await addChangeRequest(
|
await addChange(project, changeRequestDialogDetails.environment!, {
|
||||||
project,
|
feature: changeRequestDialogDetails.featureName!,
|
||||||
changeRequestDialogDetails.environment!,
|
action: 'updateEnabled',
|
||||||
{
|
payload: {
|
||||||
feature: changeRequestDialogDetails.featureName!,
|
enabled: Boolean(changeRequestDialogDetails.enabled),
|
||||||
action: 'updateEnabled',
|
},
|
||||||
payload: {
|
});
|
||||||
enabled: Boolean(changeRequestDialogDetails.enabled),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
refetchChangeRequests();
|
refetchChangeRequests();
|
||||||
setChangeRequestDialogDetails(prev => ({ ...prev, isOpen: false }));
|
setChangeRequestDialogDetails(prev => ({ ...prev, isOpen: false }));
|
||||||
setToastData({
|
setToastData({
|
||||||
@ -57,7 +53,7 @@ export const useChangeRequestToggle = (project: string) => {
|
|||||||
setToastApiError(formatUnknownError(error));
|
setToastApiError(formatUnknownError(error));
|
||||||
setChangeRequestDialogDetails(prev => ({ ...prev, isOpen: false }));
|
setChangeRequestDialogDetails(prev => ({ ...prev, isOpen: false }));
|
||||||
}
|
}
|
||||||
}, [addChangeRequest]);
|
}, [addChange]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onChangeRequestToggle,
|
onChangeRequestToggle,
|
||||||
|
@ -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
|
* @see https://plausible.io/docs/custom-event-goals#2-create-a-custom-event-goal-in-your-plausible-analytics-account
|
||||||
* @example `'download | 'invite' | 'signup'`
|
* @example `'download | 'invite' | 'signup'`
|
||||||
**/
|
**/
|
||||||
type CustomEvents = 'invite' | 'upgrade_plan_clicked';
|
type CustomEvents = 'invite' | 'upgrade_plan_clicked' | 'change_request';
|
||||||
|
|
||||||
export const usePlausibleTracker = () => {
|
export const usePlausibleTracker = () => {
|
||||||
const plausible = useContext(PlausibleContext);
|
const plausible = useContext(PlausibleContext);
|
||||||
|
Loading…
Reference in New Issue
Block a user