Merge branch 'feature/rem-events' into 'release'

More events

See merge request theappsmith/internal-tools-client!346
This commit is contained in:
Satbir Singh 2020-03-06 04:59:24 +00:00
commit 8c13eafde3
22 changed files with 328 additions and 20 deletions

View File

@ -70,7 +70,7 @@ export const updateActionSuccess = (payload: { data: RestAction }) => {
};
};
export const deleteAction = (payload: { id: string }) => {
export const deleteAction = (payload: { id: string; name: string }) => {
return {
type: ReduxActionTypes.DELETE_ACTION_INIT,
payload,

View File

@ -12,3 +12,18 @@ export const setDefaultApplicationPageSuccess = (
},
};
};
export const fetchApplications = () => {
return {
type: ReduxActionTypes.FETCH_APPLICATION_LIST_INIT,
};
};
export const fetchApplication = (applicationId: string) => {
return {
type: ReduxActionTypes.FETCH_APPLICATION_INIT,
payload: {
applicationId,
},
};
};

View File

@ -23,6 +23,10 @@ export interface ApplicationResponsePayload {
pages?: ApplicationPagePayload[];
}
export interface FetchApplicationResponse extends ApiResponse {
data: ApplicationResponsePayload & { pages: ApplicationPagePayload[] };
}
export interface FetchApplicationsResponse extends ApiResponse {
data: Array<ApplicationResponsePayload & { pages: ApplicationPagePayload[] }>;
}
@ -62,6 +66,11 @@ class ApplicationApi extends Api {
static fetchApplications(): AxiosPromise<FetchApplicationsResponse> {
return Api.get(ApplicationApi.baseURL);
}
static fetchApplication(
applicationId: string,
): AxiosPromise<FetchApplicationsResponse> {
return Api.get(ApplicationApi.baseURL + applicationId);
}
static createApplication(
request: CreateApplicationRequest,
): AxiosPromise<CreateApplicationResponse> {

View File

@ -26,6 +26,8 @@ export interface CreateDatasourceConfig {
datasourceConfiguration: {
url: string;
};
//Passed for logging purposes.
appName: string;
}
class DatasourcesApi extends API {

View File

@ -18,6 +18,7 @@ import _ from "lodash";
import { parseDynamicString } from "utils/DynamicBindingUtils";
import { DataTree } from "entities/DataTree/dataTreeFactory";
import { Theme } from "constants/DefaultTheme";
import AnalyticsUtil from "utils/AnalyticsUtil";
require("codemirror/mode/javascript/javascript");
const getBorderStyle = (
@ -277,8 +278,13 @@ class DynamicAutocompleteInput extends Component<Props, State> {
}
};
handleChange = () => {
handleChange = (instance?: any, changeObj?: any) => {
const value = this.editor.getValue();
if (changeObj && changeObj.origin === "complete") {
AnalyticsUtil.logEvent("AUTO_COMPLETE_SELECT", {
searchString: changeObj.text[0],
});
}
const inputValue = this.props.input.value;
if (this.props.input.onChange && value !== inputValue) {
this.props.input.onChange(value);
@ -312,6 +318,7 @@ class DynamicAutocompleteInput extends Component<Props, State> {
});
const shouldShow = cursorBetweenBinding && !cm.state.completionActive;
if (shouldShow) {
AnalyticsUtil.logEvent("AUTO_COMPELTE_SHOW", {});
cm.showHint(cm);
}
}

View File

@ -1,6 +1,7 @@
import React from "react";
import styled from "styled-components";
import { NavLink } from "react-router-dom";
import AnalyticsUtil from "utils/AnalyticsUtil";
type MenuBarItemProps = {
icon: Function;
@ -62,7 +63,16 @@ class NavBarItem extends React.Component<Props> {
const { title, icon, path, exact } = this.props;
return (
<ItemContainer>
<NavLink exact={exact} to={path} className={this.props.className}>
<NavLink
exact={exact}
to={path}
className={this.props.className}
onClick={() => {
AnalyticsUtil.logEvent("SIDEBAR_NAVIGATION", {
navPage: this.props.title.toUpperCase(),
});
}}
>
<React.Fragment>
<IconContainer>{icon({ width: 24, height: 24 })}</IconContainer>
{title}

View File

@ -6,6 +6,7 @@ import { AppState } from "reducers";
import { DatasourceDataState } from "reducers/entityReducers/datasourceReducer";
import _ from "lodash";
import { createDatasource } from "actions/datasourcesActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
interface ReduxStateProps {
datasources: DatasourceDataState;
@ -17,6 +18,7 @@ interface ReduxActionProps {
interface ComponentProps {
name: string;
pluginId: string;
appName: string;
}
const DatasourcesField = (
@ -55,7 +57,11 @@ const mapDispatchToProps = (
dispatch: any,
ownProps: ComponentProps,
): ReduxActionProps => ({
createDatasource: (value: string) =>
createDatasource: (value: string) => {
AnalyticsUtil.logEvent("CREATE_DATA_SOURCE_CLICK", {
appName: ownProps.appName,
dataSource: value,
});
dispatch(
createDatasource({
// Datasource name should not end with /
@ -65,8 +71,10 @@ const mapDispatchToProps = (
url: value.endsWith("/") ? value : `${value}/`,
},
pluginId: ownProps.pluginId,
appName: ownProps.appName,
}),
),
);
},
});
export default connect(mapStateToProps, mapDispatchToProps)(DatasourcesField);

View File

@ -73,6 +73,8 @@ export const ReduxActionTypes: { [key: string]: string } = {
INITIALIZE_PAGE_VIEWER_SUCCESS: "INITIALIZE_PAGE_VIEWER_SUCCESS",
FETCH_APPLICATION_LIST_INIT: "FETCH_APPLICATION_LIST_INIT",
FETCH_APPLICATION_LIST_SUCCESS: "FETCH_APPLICATION_LIST_SUCCESS",
FETCH_APPLICATION_INIT: "FETCH_APPLICATION_INIT",
FETCH_APPLICATION_SUCCESS: "FETCH_APFETCH_APPLICATION_SUCCESSPLICATION_INIT",
CREATE_APPLICATION_INIT: "CREATE_APPLICATION_INIT",
CREATE_APPLICATION_SUCCESS: "CREATE_APPLICATION_SUCCESS",
UPDATE_WIDGET_PROPERTY_VALIDATION: "UPDATE_WIDGET_PROPERTY_VALIDATION",
@ -180,6 +182,7 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
CREATE_PAGE_ERROR: "CREATE_PAGE_ERROR",
FETCH_PAGE_LIST_ERROR: "FETCH_PAGE_LIST_ERROR",
FETCH_APPLICATION_LIST_ERROR: "FETCH_APPLICATION_LIST_ERROR",
FETCH_APPLICATION_ERROR: "FETCH_APPLICATION_ERROR",
CREATE_APPLICATION_ERROR: "CREATE_APPLICATION_ERROR",
LOGIN_USER_ERROR: "LOGIN_USER_ERROR",
CREATE_USER_ERROR: "CREATE_USER_ERROR",

View File

@ -22,6 +22,7 @@ import CreateApplicationForm from "./CreateApplicationForm";
import { CREATE_APPLICATION_FORM_NAME } from "constants/forms";
import { DELETING_APPLICATION } from "constants/messages";
import { AppToaster } from "components/editorComponents/ToastComponent";
import AnalyticsUtil from "utils/AnalyticsUtil";
const ApplicationCardsWrapper = styled.div`
display: flex;
@ -64,6 +65,9 @@ class Applications extends Component<ApplicationProps> {
isAdding: this.props.isCreatingApplication,
errorAdding: this.props.createApplicationError,
formSubmitText: "Create",
onClick: () => {
AnalyticsUtil.logEvent("CREATE_APP_CLICK", {});
},
}}
search={{
placeholder: "Search",

View File

@ -94,6 +94,7 @@ interface APIFormProps {
isRunning: boolean;
isDeleting: boolean;
paginationType: PaginationType;
appName: string;
}
type Props = APIFormProps & InjectedFormProps<RestAction, APIFormProps>;
@ -153,7 +154,11 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
options={HTTP_METHOD_OPTIONS}
/>
<DatasourceWrapper>
<DatasourcesField name="datasource.id" pluginId={pluginId} />
<DatasourcesField
name="datasource.id"
pluginId={pluginId}
appName={props.appName}
/>
</DatasourceWrapper>
<DynamicTextField
placeholder="v1/method"

View File

@ -18,18 +18,21 @@ import styled from "styled-components";
import { HTTP_METHODS, PLUGIN_NAME } from "constants/ApiEditorConstants";
import _ from "lodash";
import { getPluginIdOfName } from "selectors/entitiesSelector";
import { getCurrentApplication } from "selectors/applicationSelectors";
import { ApplicationPayload } from "constants/ReduxActionConstants";
interface ReduxStateProps {
actions: ActionDataState;
apiPane: ApiPaneReduxState;
formData: RestAction;
pluginId: string | undefined;
currentApplication?: ApplicationPayload;
}
interface ReduxActionProps {
submitForm: (name: string) => void;
createAction: (values: RestAction) => void;
runAction: (id: string, paginationField: PaginationField) => void;
deleteAction: (id: string) => void;
deleteAction: (id: string, name: string) => void;
updateAction: (data: RestAction) => void;
}
@ -55,7 +58,10 @@ class ApiEditor extends React.Component<Props> {
this.props.submitForm(API_EDITOR_FORM_NAME);
};
handleDeleteClick = () => {
this.props.deleteAction(this.props.match.params.apiId);
this.props.deleteAction(
this.props.match.params.apiId,
this.props.formData.name,
);
};
handleRunClick = (paginationField?: PaginationField) => {
this.props.runAction(this.props.match.params.apiId, paginationField);
@ -96,6 +102,11 @@ class ApiEditor extends React.Component<Props> {
onSaveClick={this.handleSaveClick}
onDeleteClick={this.handleDeleteClick}
onRunClick={this.handleRunClick}
appName={
this.props.currentApplication
? this.props.currentApplication.name
: ""
}
/>
) : (
<EmptyStateContainer>
@ -111,6 +122,7 @@ const mapStateToProps = (state: AppState): ReduxStateProps => ({
pluginId: getPluginIdOfName(state, PLUGIN_NAME),
actions: state.entities.actions,
apiPane: state.ui.apiPane,
currentApplication: getCurrentApplication(state),
formData: getFormValues(API_EDITOR_FORM_NAME)(state) as RestAction,
});
@ -119,7 +131,8 @@ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
createAction: (action: RestAction) => dispatch(createActionRequest(action)),
runAction: (id: string, paginationField: PaginationField) =>
dispatch(runApiAction(id, paginationField)),
deleteAction: (id: string) => dispatch(deleteAction({ id })),
deleteAction: (id: string, name: string) =>
dispatch(deleteAction({ id, name })),
updateAction: (data: RestAction) => dispatch(updateAction({ data })),
});

View File

@ -18,6 +18,7 @@ import { getPluginIdOfName } from "selectors/entitiesSelector";
import { DEFAULT_API_ACTION, PLUGIN_NAME } from "constants/ApiEditorConstants";
import EditorSidebar from "pages/Editor/EditorSidebar";
import { getNextEntityName } from "utils/AppsmithUtils";
import AnalyticsUtil from "utils/AnalyticsUtil";
const HTTPMethod = styled.span<{ method?: string }>`
flex: 1;
@ -69,7 +70,7 @@ interface ReduxDispatchProps {
originalPageId: string,
) => void;
copyAction: (id: string, pageId: string, name: string) => void;
deleteAction: (id: string) => void;
deleteAction: (id: string, name: string) => void;
}
type Props = ReduxStateProps &
@ -99,6 +100,9 @@ class ApiSidebar extends React.Component<Props> {
.map(a => a.config.name);
const newName = getNextEntityName("Api", pageApiNames);
this.props.createAction({ ...DEFAULT_API_ACTION, name: newName, pageId });
AnalyticsUtil.logEvent("CREATE_API_CLICK", {
apiName: newName,
});
};
handleApiChange = (actionId: string) => {
@ -134,13 +138,20 @@ class ApiSidebar extends React.Component<Props> {
this.props.copyAction(itemId, destinationPageId, name);
};
handleDelete = (itemId: string) => {
this.props.deleteAction(itemId);
handleDelete = (itemId: string, itemName: string) => {
this.props.deleteAction(itemId, itemName);
};
renderItem = (action: RestAction) => {
return (
<ActionItem>
<ActionItem
onClick={() => {
AnalyticsUtil.logEvent("API_SELECT", {
apiId: action.id,
apiName: action.name,
});
}}
>
{action.actionConfiguration ? (
<HTTPMethod method={action.actionConfiguration.httpMethod}>
{action.actionConfiguration.httpMethod}
@ -203,7 +214,8 @@ const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
),
copyAction: (id: string, destinationPageId: string, name: string) =>
dispatch(copyActionRequest({ id, destinationPageId, name })),
deleteAction: (id: string) => dispatch(deleteAction({ id })),
deleteAction: (id: string, name: string) =>
dispatch(deleteAction({ id, name })),
});
export default connect(mapStateToProps, mapDispatchToProps)(ApiSidebar);

View File

@ -159,7 +159,7 @@ type EditorSidebarComponentProps = {
onItemSelected: (itemId: string) => void;
moveItem: (itemId: string, destinationPageId: string) => void;
copyItem: (itemId: string, destinationPageId: string) => void;
deleteItem: (itemId: string) => void;
deleteItem: (itemId: string, itemName: string) => void;
};
type Props = ReduxStateProps &
@ -389,6 +389,7 @@ class EditorSidebar extends React.Component<Props, State> {
onSelect: () =>
this.props.deleteItem(
item.id,
item.name,
),
label: "Delete",
intent: "danger",

View File

@ -27,9 +27,12 @@ import {
import {
ReduxActionTypes,
PageListPayload,
ApplicationPayload,
} from "constants/ReduxActionConstants";
import { Dialog, Classes, AnchorButton } from "@blueprintjs/core";
import { initEditor } from "actions/initActions";
import { getCurrentApplication } from "selectors/applicationSelectors";
import AnalyticsUtil from "utils/AnalyticsUtil";
type EditorProps = {
currentPageName?: string;
@ -49,6 +52,7 @@ type EditorProps = {
errorPublishing: boolean;
publishedTime?: string;
isPageSwitching: boolean;
currentApplication?: ApplicationPayload;
} & RouteComponentProps<BuilderRouteParams>;
class Editor extends Component<EditorProps> {
@ -80,6 +84,14 @@ class Editor extends Component<EditorProps> {
handlePublish = () => {
if (this.props.currentApplicationId) {
this.props.publishApplication(this.props.currentApplicationId);
const appName = this.props.currentApplication
? this.props.currentApplication.name
: "";
AnalyticsUtil.logEvent("PUBLISH_APP", {
appId: this.props.currentApplicationId,
appName: appName,
});
}
};
handleCreatePage = (pageName: string) => {
@ -152,6 +164,7 @@ const mapStateToProps = (state: AppState) => ({
currentPageName: state.ui.editor.currentPageName,
isSaving: getIsPageSaving(state),
currentApplicationId: getCurrentApplicationId(state),
currentApplication: getCurrentApplication(state),
currentPageId: getCurrentPageId(state),
currentLayoutId: getCurrentLayoutId(state),
pages: getPageList(state),

View File

@ -28,6 +28,7 @@ type SubHeaderProps = {
formSubmitIntent: string;
errorAdding?: string;
formSubmitText: string;
onClick: () => void;
};
search?: {
placeholder: string;
@ -50,6 +51,7 @@ export const ApplicationsSubHeader = (props: SubHeaderProps) => {
text={props.add.title}
icon="plus"
title={props.add.title}
onClick={props.add.onClick}
filled
intent="primary"
/>

View File

@ -49,6 +49,17 @@ const applicationsReducer = createReducer(initialState, {
applicationList: action.payload,
isFetchingApplications: false,
}),
[ReduxActionTypes.FETCH_APPLICATION_INIT]: (
state: ApplicationsReduxState,
) => ({ ...state, isFetchingApplication: true }),
[ReduxActionTypes.FETCH_APPLICATION_SUCCESS]: (
state: ApplicationsReduxState,
action: ReduxAction<{ applicationList: ApplicationPayload[] }>,
) => ({
...state,
currentApplication: action.payload,
isFetchingApplication: false,
}),
[ReduxActionTypes.CREATE_APPLICATION_INIT]: (
state: ApplicationsReduxState,
) => ({
@ -93,6 +104,7 @@ export interface ApplicationsReduxState {
creatingApplication: boolean;
createApplicationError?: string;
deletingApplication: boolean;
currentApplication?: ApplicationPayload;
}
export default applicationsReducer;

View File

@ -71,6 +71,7 @@ import {
getApplicationViewerPageURL,
} from "constants/routes";
import { ToastType } from "react-toastify";
import AnalyticsUtil from "utils/AnalyticsUtil";
export const getAction = (
state: AppState,
@ -85,6 +86,25 @@ const createActionResponse = (response: ActionApiResponse): ActionResponse => ({
...response.clientMeta,
});
function getCurrentPageNameByActionId(
state: AppState,
actionId: string,
): string {
const action = state.entities.actions.find(action => {
return action.config.id === actionId;
});
const pageId = action ? action.config.pageId : "";
return getPageNameByPageId(state, pageId);
}
function getPageNameByPageId(state: AppState, pageId: string): string {
const page = state.entities.pageList.pages.find(
page => page.pageId === pageId,
);
const pageName = page ? page.pageName : "";
return pageName;
}
const createActionErrorResponse = (
response: ActionApiResponse,
): ActionResponse => ({
@ -284,6 +304,17 @@ export function* createActionSaga(actionPayload: ReduxAction<RestAction>) {
message: `${actionPayload.payload.name} Action created`,
type: ToastType.SUCCESS,
});
const pageName = yield select(
getCurrentPageNameByActionId,
response.data.id,
);
AnalyticsUtil.logEvent("CREATE_API", {
apiId: response.data.id,
apiName: response.data.name,
pageName: pageName,
});
yield put(createActionSuccess(response.data));
}
} catch (error) {
@ -351,6 +382,17 @@ export function* updateActionSaga(
message: `${actionPayload.payload.data.name} Action updated`,
type: ToastType.SUCCESS,
});
const pageName = yield select(
getCurrentPageNameByActionId,
response.data.id,
);
AnalyticsUtil.logEvent("SAVE_API", {
apiId: response.data.id,
apiName: response.data.name,
pageName: pageName,
});
yield put(updateActionSuccess({ data: response.data }));
yield put(runApiAction(data.id));
}
@ -362,9 +404,12 @@ export function* updateActionSaga(
}
}
export function* deleteActionSaga(actionPayload: ReduxAction<{ id: string }>) {
export function* deleteActionSaga(
actionPayload: ReduxAction<{ id: string; name: string }>,
) {
try {
const id = actionPayload.payload.id;
const name = actionPayload.payload.name;
const response: GenericApiResponse<RestAction> = yield ActionAPI.deleteAction(
id,
);
@ -374,6 +419,12 @@ export function* deleteActionSaga(actionPayload: ReduxAction<{ id: string }>) {
message: `${response.data.name} Action deleted`,
type: ToastType.SUCCESS,
});
const pageName = yield select(getCurrentPageNameByActionId, id);
AnalyticsUtil.logEvent("DELETE_API", {
apiName: name,
pageName: pageName,
apiID: id,
});
yield put(deleteActionSuccess({ id }));
}
} catch (error) {
@ -432,6 +483,17 @@ export function* runApiActionSaga(
payload = createActionErrorResponse(response);
}
const id = values.id || "DRY_RUN";
const pageName = yield select(getCurrentPageNameByActionId, values.id);
AnalyticsUtil.logEvent("RUN_API", {
apiId: values.id,
apiName: values.name,
pageName: pageName,
responseTime: response.clientMeta.duration,
apiType: "INTERNAL",
});
yield put({
type: ReduxActionTypes.RUN_API_SUCCESS,
payload: { [id]: payload },
@ -496,6 +558,12 @@ function* moveActionSaga(
type: ToastType.SUCCESS,
});
}
const pageName = yield select(getPageNameByPageId, response.data.pageId);
AnalyticsUtil.logEvent("MOVE_API", {
apiName: response.data.name,
pageName: pageName,
apiID: response.data.id,
});
yield put(moveActionSuccess(response.data));
} catch (e) {
AppToaster.show({
@ -537,6 +605,13 @@ function* copyActionSaga(
type: ToastType.SUCCESS,
});
}
const pageName = yield select(getPageNameByPageId, response.data.pageId);
AnalyticsUtil.logEvent("DUPLICATE_API", {
apiName: response.data.name,
pageName: pageName,
apiID: response.data.id,
});
yield put(copyActionSuccess(response.data));
} catch (e) {
AppToaster.show({

View File

@ -25,6 +25,7 @@ import history from "utils/history";
import { BUILDER_PAGE_URL } from "constants/routes";
import { AppState } from "reducers";
import { setDefaultApplicationPageSuccess } from "actions/applicationActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
export function* publishApplicationSaga(
requestAction: ReduxAction<PublishApplicationRequest>,
) {
@ -85,6 +86,32 @@ export function* fetchApplicationListSaga() {
}
}
export function* fetchApplicationSaga(
action: ReduxAction<{
applicationId: string;
}>,
) {
try {
const applicationId: string = action.payload.applicationId;
const response: FetchApplicationsResponse = yield call(
ApplicationApi.fetchApplication,
applicationId,
);
yield put({
type: ReduxActionTypes.FETCH_APPLICATION_SUCCESS,
payload: response.data,
});
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
payload: {
error,
},
});
}
}
export function* setDefaultApplicationPageSaga(
action: ReduxAction<SetDefaultPageRequest>,
) {
@ -188,6 +215,9 @@ export function* createApplicationSaga(
pageCount: response.data.pages ? response.data.pages.length : 0,
defaultPageId: getDefaultPageId(response.data.pages),
};
AnalyticsUtil.logEvent("CREATE_APP", {
appName: application.name,
});
yield put({
type: ReduxActionTypes.CREATE_APPLICATION_SUCCESS,
payload: application,
@ -224,6 +254,7 @@ export default function* applicationSagas() {
ReduxActionTypes.FETCH_APPLICATION_LIST_INIT,
fetchApplicationListSaga,
),
takeLatest(ReduxActionTypes.FETCH_APPLICATION_INIT, fetchApplicationSaga),
takeLatest(ReduxActionTypes.CREATE_APPLICATION_INIT, createApplicationSaga),
takeLatest(
ReduxActionTypes.SET_DEFAULT_APPLICATION_PAGE_INIT,

View File

@ -12,6 +12,7 @@ import DatasourcesApi, {
} from "api/DatasourcesApi";
import { API_EDITOR_FORM_NAME } from "constants/forms";
import { validateResponse } from "./ErrorSagas";
import AnalyticsUtil from "utils/AnalyticsUtil";
function* fetchDatasourcesSaga() {
try {
@ -40,6 +41,10 @@ function* createDatasourceSaga(
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AnalyticsUtil.logEvent("SAVE_DATA_SOURCE", {
dataSourceName: actionPayload.payload.name,
appName: actionPayload.payload.appName,
});
yield put({
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
payload: response.data,

View File

@ -1,4 +1,4 @@
import { all, put, takeLatest, take } from "redux-saga/effects";
import { all, put, takeLatest, take, select } from "redux-saga/effects";
import {
ReduxAction,
ReduxActionTypes,
@ -10,6 +10,9 @@ import { fetchPageList } from "actions/pageActions";
import { fetchDatasources } from "actions/datasourcesActions";
import { fetchPlugins } from "actions/pluginActions";
import { fetchActions } from "actions/actionActions";
import { fetchApplication } from "actions/applicationActions";
import { AppState } from "reducers";
import AnalyticsUtil from "utils/AnalyticsUtil";
function* initializeEditorSaga(
initializeEditorAction: ReduxAction<InitializeEditorPayload>,
@ -18,6 +21,7 @@ function* initializeEditorSaga(
// Step 1: Start getting all the data needed by the
yield all([
put(fetchPlugins()),
put(fetchApplication(applicationId)),
put(fetchPageList(applicationId)),
put(fetchEditorConfigs()),
put(fetchActions(applicationId)),
@ -26,11 +30,25 @@ function* initializeEditorSaga(
// Step 2: Wait for all data to be in the state
yield all([
take(ReduxActionTypes.FETCH_PLUGINS_SUCCESS),
take(ReduxActionTypes.FETCH_APPLICATION_SUCCESS),
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS),
]);
const currentApplication = yield select(
(state: AppState) => state.ui.applications.currentApplication,
);
const appName = currentApplication ? currentApplication.name : "";
const appId = currentApplication ? currentApplication.id : "";
AnalyticsUtil.setAppData(appId, appName);
AnalyticsUtil.logEvent("EDITOR_OPEN", {
appId: appId,
appName: appName,
});
// Step 6: Notify UI that the editor is ready to go
yield put({
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
@ -44,13 +62,27 @@ export function* initializeAppViewerSaga(
yield all([
put(fetchActions(applicationId)),
put(fetchPageList(applicationId)),
put(fetchApplication(applicationId)),
]);
yield all([
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
take(ReduxActionTypes.FETCH_APPLICATION_SUCCESS),
]);
const currentApplication = yield select(
(state: AppState) => state.ui.applications.currentApplication,
);
const appName = currentApplication ? currentApplication.name : "";
const appId = currentApplication ? currentApplication.id : "";
AnalyticsUtil.setAppData(appId, appName);
AnalyticsUtil.logEvent("PREVIEW_APP", {
appId: appId,
appName: appName,
});
yield put({
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
});

View File

@ -15,6 +15,8 @@ const fuzzySearchOptions = {
const getApplicationsState = (state: AppState) => state.ui.applications;
const getApplications = (state: AppState) =>
state.ui.applications.applicationList;
export const getCurrentApplication = (state: AppState) =>
state.ui.applications.currentApplication;
const getApplicationSearchKeyword = (state: AppState) =>
state.ui.applications.searchKeyword;
export const getIsDeletingApplication = (state: AppState) =>

View File

@ -16,7 +16,25 @@ export type EventName =
| "CREATE_PAGE"
| "PAGE_RENAME"
| "PAGE_SWITCH"
| "DELETE_PAGE";
| "DELETE_PAGE"
| "SIDEBAR_NAVIGATION"
| "PUBLISH_APP"
| "PREVIEW_APP"
| "EDITOR_OPEN"
| "CREATE_API"
| "SAVE_API"
| "RUN_API"
| "DELETE_API"
| "DUPLICATE_API"
| "MOVE_API"
| "API_SELECT"
| "CREATE_API_CLICK"
| "AUTO_COMPELTE_SHOW"
| "AUTO_COMPLETE_SELECT"
| "CREATE_APP_CLICK"
| "CREATE_APP"
| "CREATE_DATA_SOURCE_CLICK"
| "SAVE_DATA_SOURCE";
export type Gender = "MALE" | "FEMALE";
export interface User {
@ -27,6 +45,14 @@ export interface User {
}
class AnalyticsUtil {
static user: any = {};
static appData: {
appId: string;
appName: string;
} = {
appId: "",
appName: "",
};
static initializeHotjar(id: string, sv: string) {
(function init(h: any, o: any, t: any, j: any, a?: any, r?: any) {
h.hj =
@ -105,19 +131,40 @@ class AnalyticsUtil {
static logEvent(eventName: EventName, eventData: any) {
const windowDoc: any = window;
let finalEventData = eventData;
const userData = AnalyticsUtil.user;
const appData = AnalyticsUtil.appData;
if (userData) {
finalEventData = {
...finalEventData,
userData: {
email: userData.email,
currentOrgId: userData.currentOrganizationId,
...appData,
},
};
}
if (windowDoc.analytics) {
windowDoc.analytics.track(eventName, eventData);
windowDoc.analytics.track(eventName, finalEventData);
} else {
console.log("Event fired", eventName, eventData);
console.log("Event fired", eventName, finalEventData);
}
}
static identifyUser(userId: string, userData: User) {
const windowDoc: any = window;
AnalyticsUtil.user = userData;
if (windowDoc.analytics) {
windowDoc.analytics.identify(userId, userData);
}
}
static setAppData(appId: string, appName: string) {
AnalyticsUtil.appData = {
appId: appId,
appName: appName,
};
}
}
export default AnalyticsUtil;