feat: add analytics for drag and drop building blocks (#32699)

## Description
> [!TIP]  
Add events to track the dragging of building blocks, dropping of blocks,
and the time taken from drag till drop is complete.

**Drag Event**
```
AnalyticsUtil.logEvent("DRAG_BUILDING_BLOCK_INITIATED", {
        applicationId,
        workspaceId,
        source: "explorer",
        eventData: {
          buildingBlockName: props.details.displayName,
        },
      });
```

**Drop Event**
```
AnalyticsUtil.logEvent("DROP_BUILDING_BLOCK_INITIATED", {
        applicationId,
        workspaceId,
        source: "explorer",
        eventData: {
          buildingBlockName: props.details.displayName,
        },
      });

AnalyticsUtil.logEvent("DROP_BUILDING_BLOCK_COMPLETED", {
        applicationId,
        workspaceId,
        source: "explorer",
        eventData: {
          buildingBlockName: dragDetails.newWidget.displayName,
          timeTakenToCompletion: timeTakenTo CompleteValueInSeconds,
          timeTakenToDropWidgets: timeTakenValueInSeconds
        },
      });
```

Fixes #32492  

## Automation

/ok-to-test tags="@tag.Widget"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/8785306375>
> Commit: 8316506b039256ad6d171a3a81ddaec56cecdfc2
> Cypress dashboard url: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=8785306375&attempt=1"
target="_blank">Click here!</a>

<!-- end of auto-generated comment: Cypress test results  -->






























<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced analytics tracking for building block drag-and-drop
operations.
- Enhanced functionality for adding and managing building blocks within
the application.
- **Refactor**
- Updated action type constants for better consistency in handling
building block operations.
- **Bug Fixes**
- Improved logic for setting the start time of building block drag
operations to ensure accurate tracking.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Anagh Hegde <anagh@appsmith.com>
Co-authored-by: Rahul Barwal <rahul.barwal@appsmith.com>
This commit is contained in:
Jacques Ikot 2024-04-22 15:58:37 +01:00 committed by GitHub
parent dc7bd0298f
commit 60d45ea6ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 139 additions and 11 deletions

View File

@ -928,6 +928,8 @@ const ActionTypes = {
CLOSE_JS_ACTION_TAB: "CLOSE_JS_ACTION_TAB",
CLOSE_JS_ACTION_TAB_SUCCESS: "CLOSE_JS_ACTION_TAB_SUCCESS",
CLOSE_QUERY_ACTION_TAB: "CLOSE_QUERY_ACTION_TAB",
SET_BUILDING_BLOCK_DRAG_START_TIME: "SET_BUILDING_BLOCK_DRAG_START_TIME",
RESET_BUILDING_BLOCK_DRAG_START_TIME: "RESET_BUILDING_BLOCK_DRAG_START_TIME",
CLOSE_QUERY_ACTION_TAB_SUCCESS: "CLOSE_QUERY_ACTION_TAB_SUCCESS",
};

View File

@ -353,6 +353,7 @@ export type EventName =
| "TEMPLATE_ADD_PAGE_FROM_TEMPLATE_FLOW"
| HOMEPAGE_CREATE_APP_FROM_TEMPLATE_EVENTS
| "EDITOR_MODE_CHANGE"
| BUILDING_BLOCKS_EVENTS
| "VISIT_SELF_HOST_DOCS";
type HOMEPAGE_CREATE_APP_FROM_TEMPLATE_EVENTS =
@ -365,6 +366,11 @@ export type CANVAS_STARTER_BUILDING_BLOCK_EVENTS =
| "STARTER_BUILDING_BLOCK_CONNECT_DATA_CLICK"
| "STARTER_BUILDING_BLOCK_SEE_MORE_CLICK";
export type BUILDING_BLOCKS_EVENTS =
| "DROP_BUILDING_BLOCK_INITIATED"
| "DROP_BUILDING_BLOCK_COMPLETED"
| "DRAG_BUILDING_BLOCK_INITIATED";
export type ONBOARDING_FLOW_EVENTS =
| "CREATE_APP_FROM_TEMPLATE"
| "CREATE_APP_FROM_SCRATCH"

View File

@ -5,6 +5,10 @@ import { useWidgetDragResize } from "utils/hooks/dragResizeHooks";
import AnalyticsUtil from "@appsmith/utils/AnalyticsUtil";
import { generateReactKey } from "utils/generators";
import { Text } from "design-system";
import { BUILDING_BLOCK_EXPLORER_TYPE } from "constants/WidgetConstants";
import { useSelector } from "react-redux";
import { getCurrentApplicationId } from "selectors/editorSelectors";
import { getCurrentWorkspaceId } from "@appsmith/selectors/selectedWorkspaceSelectors";
interface CardProps {
details: WidgetCardProps;
@ -72,15 +76,28 @@ const THUMBNAIL_HEIGHT = 76;
const THUMBNAIL_WIDTH = 72;
function WidgetCard(props: CardProps) {
const applicationId = useSelector(getCurrentApplicationId);
const workspaceId = useSelector(getCurrentWorkspaceId);
const { setDraggingNewWidget } = useWidgetDragResize();
const onDragStart = (e: any) => {
e.preventDefault();
e.stopPropagation();
AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", {
widgetType: props.details.type,
widgetName: props.details.displayName,
});
if (props.details.type === BUILDING_BLOCK_EXPLORER_TYPE) {
AnalyticsUtil.logEvent("DRAG_BUILDING_BLOCK_INITIATED", {
applicationId,
workspaceId,
source: "explorer",
eventData: {
buildingBlockName: props.details.displayName,
},
});
} else {
AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", {
widgetType: props.details.type,
widgetName: props.details.displayName,
});
}
setDraggingNewWidget &&
setDraggingNewWidget(true, {
...props.details,

View File

@ -1,3 +1,4 @@
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
import {
ReduxActionErrorTypes,
ReduxActionTypes,
@ -33,10 +34,28 @@ const buildingBlockReducer = createReducer(initialState, {
isDraggingBuildingBlockToCanvas: false,
};
},
[ReduxActionTypes.SET_BUILDING_BLOCK_DRAG_START_TIME]: (
state: BuildingBlocksReduxState,
action: ReduxAction<{ startTime: number }>,
) => {
return {
...state,
buildingBlockDragStartTimestamp: action.payload.startTime,
};
},
[ReduxActionTypes.RESET_BUILDING_BLOCK_DRAG_START_TIME]: (
state: BuildingBlocksReduxState,
) => {
return {
...state,
buildingBlockDragStartTimestamp: null,
};
},
});
export interface BuildingBlocksReduxState {
isDraggingBuildingBlocksToCanvas: boolean;
buildingBlockDragStartTimestamp?: number;
}
export default buildingBlockReducer;

View File

@ -3,6 +3,7 @@ import {
ReduxActionErrorTypes,
ReduxActionTypes,
} from "@appsmith/constants/ReduxActionConstants";
import AnalyticsUtil from "@appsmith/utils/AnalyticsUtil";
import { BlueprintOperationTypes } from "WidgetProvider/constants";
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions";
import type { WidgetAddChild } from "actions/pageActions";
@ -45,12 +46,13 @@ import {
} from "sagas/selectors";
import {
getCanvasWidth,
getCurrentApplicationId,
getIsAutoLayoutMobileBreakPoint,
getMainCanvasProps,
getOccupiedSpacesSelectorForContainer,
} from "selectors/editorSelectors";
import { getLayoutSystemType } from "selectors/layoutSystemSelectors";
import AnalyticsUtil from "@appsmith/utils/AnalyticsUtil";
import { initiateBuildingBlockDropEvent } from "utils/buildingBlockUtils";
import { collisionCheckPostReflow } from "utils/reflowHookUtils";
import type { WidgetProps } from "widgets/BaseWidget";
@ -157,6 +159,8 @@ function* addBuildingBlockAndMoveWidgetsSaga(
canvasId: string;
}>,
) {
const applicationId: string = yield select(getCurrentApplicationId);
const workspaceId: string = yield select(getCurrentApplicationId);
const dragDetails: DragDetails = yield select(getDragDetails);
const buildingblockName = dragDetails.newWidget.displayName;
const skeletonWidgetName = `loading_${buildingblockName
@ -177,6 +181,11 @@ function* addBuildingBlockAndMoveWidgetsSaga(
},
},
});
yield call(initiateBuildingBlockDropEvent, {
applicationId,
workspaceId,
buildingblockName,
});
const skeletonWidget: FlattenedWidgetProps = yield select(
getWidgetByName,

View File

@ -92,6 +92,10 @@ import { SelectionRequestType } from "./WidgetSelectUtils";
import type { ActionDataState } from "@appsmith/reducers/entityReducers/actionsReducer";
import type { WidgetLayoutPositionInfo } from "layoutSystems/anvil/utils/layouts/widgetPositionUtils";
import { getBuildingBlockDragStartTimestamp } from "selectors/buildingBlocksSelectors";
import { initiateBuildingBlockDropEvent } from "utils/buildingBlockUtils";
import AnalyticsUtil from "@appsmith/utils/AnalyticsUtil";
const WidgetTypes = WidgetFactory.widgetTypes;
export interface GeneratedWidgetPayload {
@ -650,9 +654,13 @@ export function* addBuildingBlockToApplication(
try {
const dragDetails: DragDetails = yield select(getDragDetails);
const applicationId: string = yield select(getCurrentApplicationId);
const workspaceId: string = yield select(getCurrentWorkspaceId);
const actionsBeforeAddingBuildingBlock: ActionDataState =
yield select(getActions);
const existingCopiedWidgets: unknown = yield call(getCopiedWidgets);
const buildingBlockDragStartTimestamp: number = yield select(
getBuildingBlockDragStartTimestamp,
);
// start loading for dragging building blocks
yield put({
@ -687,8 +695,10 @@ export function* addBuildingBlockToApplication(
});
yield put(pasteWidget(false, mousePosition));
const timeTakenToDropWidgetsInSeconds =
(Date.now() - buildingBlockDragStartTimestamp) / 1000;
yield call(postPageAdditionSaga, applicationId);
// remove selecting of recently imported widgets
// remove selecting of recently pasted widgets caused by pasteWidget
yield put(selectWidgetInitAction(SelectionRequestType.Empty));
// stop loading after pasting process is complete
@ -699,10 +709,34 @@ export function* addBuildingBlockToApplication(
const actionsAfterAddingBuildingBlocks: ActionDataState =
yield select(getActions);
yield runNewlyCreatedActions(
actionsBeforeAddingBuildingBlock,
actionsAfterAddingBuildingBlocks,
);
if (
response.data.onPageLoadActions &&
response.data.onPageLoadActions.length > 0
) {
yield runNewlyCreatedActions(
actionsBeforeAddingBuildingBlock,
actionsAfterAddingBuildingBlocks,
);
}
const timeTakenToCompleteInMs = buildingBlockDragStartTimestamp
? Date.now() - buildingBlockDragStartTimestamp
: 0;
const timeTakenToCompleteInSeconds = timeTakenToCompleteInMs / 1000;
AnalyticsUtil.logEvent("DROP_BUILDING_BLOCK_COMPLETED", {
applicationId,
workspaceId,
source: "explorer",
eventData: {
buildingBlockName: dragDetails.newWidget.displayName,
timeTakenToCompletion: timeTakenToCompleteInSeconds,
timeTakenToDropWidgets: timeTakenToDropWidgetsInSeconds,
},
});
yield put({
type: ReduxActionTypes.RESET_BUILDING_BLOCK_DRAG_START_TIME,
});
if (existingCopiedWidgets) {
yield call(saveCopiedWidgets, JSON.stringify(existingCopiedWidgets));
@ -725,6 +759,8 @@ export function* addBuildingBlockToApplication(
}
function* addBuildingBlockSaga(addEntityAction: ReduxAction<WidgetAddChild>) {
const applicationId: string = yield select(getCurrentApplicationId);
const workspaceId: string = yield select(getCurrentWorkspaceId);
const dragDetails: DragDetails = yield select(getDragDetails);
const buildingblockName = dragDetails.newWidget.displayName;
const skeletonWidgetName = `loading_${buildingblockName
@ -743,6 +779,13 @@ function* addBuildingBlockSaga(addEntityAction: ReduxAction<WidgetAddChild>) {
shouldReplay: false,
},
};
yield call(initiateBuildingBlockDropEvent, {
applicationId,
workspaceId,
buildingblockName,
});
yield call(addChildSaga, addSkeletonWidgetAction);
const skeletonWidget: FlattenedWidgetProps = yield select(
getWidgetByName,

View File

@ -2,3 +2,6 @@ import type { AppState } from "@appsmith/reducers";
export const isDraggingBuildingBlockToCanvas = (state: AppState) =>
state.ui.buildingBlocks.isDraggingBuildingBlocksToCanvas;
export const getBuildingBlockDragStartTimestamp = (state: AppState) =>
state.ui.buildingBlocks.buildingBlockDragStartTimestamp;

View File

@ -5,6 +5,7 @@ import { getFetchedWorkspaces } from "@appsmith/selectors/workspaceSelectors";
import { hasCreateNewAppPermission } from "@appsmith/utils/permissionHelpers";
import type { FilterKeys, Template } from "api/TemplatesApi";
import {
BUILDING_BLOCK_EXPLORER_TYPE,
DEFAULT_COLUMNS_FOR_EXPLORER_BUILDING_BLOCKS,
DEFAULT_ROWS_FOR_EXPLORER_BUILDING_BLOCKS,
WIDGET_TAGS,
@ -80,7 +81,7 @@ export const getBuildingBlockExplorerCards = createSelector(
columns:
buildingBlock.templateGridColumnSize ||
DEFAULT_COLUMNS_FOR_EXPLORER_BUILDING_BLOCKS,
type: "BUILDING_BLOCK",
type: BUILDING_BLOCK_EXPLORER_TYPE,
displayName: buildingBlock.title,
icon:
buildingBlock.screenshotUrls.length > 1

View File

@ -0,0 +1,28 @@
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import AnalyticsUtil from "@appsmith/utils/AnalyticsUtil";
import { put } from "redux-saga/effects";
interface BuildingBlockDropInitiateEvent {
applicationId: string;
workspaceId: string;
buildingblockName: string;
}
export function* initiateBuildingBlockDropEvent({
applicationId,
buildingblockName,
workspaceId,
}: BuildingBlockDropInitiateEvent) {
AnalyticsUtil.logEvent("DROP_BUILDING_BLOCK_INITIATED", {
applicationId,
workspaceId,
source: "explorer",
eventData: {
buildingBlockName: buildingblockName,
},
});
yield put({
type: ReduxActionTypes.SET_BUILDING_BLOCK_DRAG_START_TIME,
payload: { startTime: Date.now() },
});
}