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:
parent
dc7bd0298f
commit
60d45ea6ff
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
28
app/client/src/utils/buildingBlockUtils.ts
Normal file
28
app/client/src/utils/buildingBlockUtils.ts
Normal 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() },
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user