From 69a08bdf3e26d933715a6f779fb92d3b345fdc42 Mon Sep 17 00:00:00 2001 From: Aman Chaturvedi Date: Wed, 18 Jan 2023 16:39:24 +0530 Subject: [PATCH] Click stream events added (#62) Click stream events added --- App.tsx | 2 + ProtectedRouter.tsx | 8 +- config/dev/config.js | 3 +- config/qa/config.js | 3 +- src/action/authActions.ts | 4 +- src/action/dataActions.ts | 7 +- src/common/Constants.ts | 102 ++++++++++++++++ src/common/ErrorBoundary.tsx | 10 +- src/components/form/Submit.tsx | 9 +- .../form/components/CheckboxGroup.tsx | 7 +- src/components/form/components/Dropdown.tsx | 7 +- .../form/components/ImageUpload.tsx | 8 +- .../form/components/RadioButton.tsx | 7 +- src/components/form/components/Rating.tsx | 7 +- src/components/form/components/TextArea.tsx | 7 +- src/components/form/components/TextInput.tsx | 7 +- src/components/form/index.tsx | 65 +++++++--- .../allCasesFilters/FiltersContainer.tsx | 44 ++++--- src/components/utlis/apiHelper.ts | 33 +++++- src/components/utlis/commonFunctions.ts | 11 +- src/constants/Global.ts | 4 +- src/constants/config.js | 3 +- src/hooks/janusApi.ts | 24 ++++ src/hooks/useIsOnline.tsx | 2 + src/hooks/useNativeButton.tsx | 47 ++++++++ src/reducer/allCasesSlice.ts | 12 +- src/reducer/commonSlice.ts | 15 ++- src/reducer/userSlice.ts | 16 ++- src/screens/Profile/index.tsx | 18 ++- src/screens/allCases/AllCases.tsx | 19 ++- src/screens/allCases/CasesActionButtons.tsx | 6 +- src/screens/allCases/ListItem.tsx | 3 + src/screens/allCases/index.tsx | 5 +- src/screens/caseDetails/CaseDetails.tsx | 20 +++- .../journeyStepper/CallingBottomSheet.tsx | 31 ++++- .../journeyStepper/TaskContent.tsx | 15 ++- .../journeyStepper/TaskStepper.tsx | 3 + src/screens/login/OtpInput.tsx | 15 ++- src/screens/login/index.tsx | 14 ++- src/screens/todoList/TodoList.tsx | 11 +- src/services/clickstreamEventService.ts | 112 ++++++++++++++++++ src/wmDB/const.ts | 5 +- src/wmDB/dao/ClickStreamEventsDAO.ts | 94 +++++++++++++++ src/wmDB/db.ts | 3 +- src/wmDB/index.ts | 3 +- src/wmDB/model/ClickstreamEvents.ts | 17 +++ src/wmDB/schema.ts | 20 +++- 47 files changed, 796 insertions(+), 92 deletions(-) create mode 100644 src/hooks/janusApi.ts create mode 100644 src/hooks/useNativeButton.tsx create mode 100644 src/services/clickstreamEventService.ts create mode 100644 src/wmDB/dao/ClickStreamEventsDAO.ts create mode 100644 src/wmDB/model/ClickstreamEvents.ts diff --git a/App.tsx b/App.tsx index 62f0c941..08d90bfa 100644 --- a/App.tsx +++ b/App.tsx @@ -13,10 +13,12 @@ import ErrorBoundary from './src/common/ErrorBoundary'; import * as Sentry from '@sentry/browser'; import { SENTRY_DSN } from './src/constants/config'; +import useNativeButtons from './src/hooks/useNativeButton'; Sentry.init({ dsn: SENTRY_DSN }); const App = () => { + useNativeButtons(); return ( { const {isLoggedIn, deviceId, sessionDetails} = user; - // for setting user token in global.ts for api calling's - setGlobalUserData(sessionDetails?.sessionToken, deviceId); interactionsHandler() const dispatch = useAppDispatch(); - + // Firestore listener hook useFirestoreUpdates(); - + if (!deviceId) { getUniqueId().then(id => dispatch(setDeviceId(id))); } + // for setting user token in global.ts for api calling's + setGlobalUserData(sessionDetails?.sessionToken, deviceId, user?.user?.referenceId); const getScreenFocusListenerObj = ({route}: {route: RouteProp}) => ({ focus: () => { diff --git a/config/dev/config.js b/config/dev/config.js index 39392cd3..9a36e940 100644 --- a/config/dev/config.js +++ b/config/dev/config.js @@ -1,2 +1,3 @@ export const BASE_AV_APP_URL = 'https://dev-longhorn-server.np.navi-tech.in/av'; -export const SENTRY_DSN = 'https://877396e88a2b4f78b911016c64f9121a@glitchtip.cmd.navi-tech.in/155'; \ No newline at end of file +export const SENTRY_DSN = 'https://877396e88a2b4f78b911016c64f9121a@glitchtip.cmd.navi-tech.in/155'; +export const JANUS_SERVICE_URL = 'https://dev-longhorn-portal.np.navi-tech.in/api/events/json'; \ No newline at end of file diff --git a/config/qa/config.js b/config/qa/config.js index d1bc2d33..2dde6720 100644 --- a/config/qa/config.js +++ b/config/qa/config.js @@ -1,2 +1,3 @@ export const BASE_AV_APP_URL = 'https://qa-longhorn-server.np.navi-tech.in/av'; -export const SENTRY_DSN = 'https://877396e88a2b4f78b911016c64f9121a@glitchtip.cmd.navi-tech.in/155'; \ No newline at end of file +export const SENTRY_DSN = 'https://877396e88a2b4f78b911016c64f9121a@glitchtip.cmd.navi-tech.in/155'; +export const JANUS_SERVICE_URL = 'https://qa-longhorn-portal.np.navi-tech.in/api/events/json'; \ No newline at end of file diff --git a/src/action/authActions.ts b/src/action/authActions.ts index 28e02eee..9b0d90b9 100644 --- a/src/action/authActions.ts +++ b/src/action/authActions.ts @@ -105,5 +105,7 @@ export const logout = () => (dispatch: AppDispatch) => { ); dispatch(resetCasesData()); }) - .catch(err => console.log(err)); + .catch(err => { + console.log(err) + }); }; diff --git a/src/action/dataActions.ts b/src/action/dataActions.ts index 248d8798..e543ac1c 100644 --- a/src/action/dataActions.ts +++ b/src/action/dataActions.ts @@ -18,6 +18,8 @@ import { } from '../reducer/allCasesSlice'; import { ICaseItem } from '../screens/allCases/interface'; import { AppDispatch } from '../store/store'; +import { addClickstreamEvent } from '../services/clickstreamEventService'; +import { CLICKSTREAM_EVENT_NAMES } from '../common/Constants'; import { logError } from '../components/utlis/errorUtils'; export const getAllCases = @@ -67,7 +69,7 @@ export const getSingleCaseDetail = }; export const postPinnedList = - (pinnedCases: ICaseItem[], updatedCaseList: ICaseItem[]) => + (pinnedCases: ICaseItem[], updatedCaseList: ICaseItem[], type?: string) => (dispatch: AppDispatch) => { dispatch(setTodoListOffline(updatedCaseList)); navigateToScreen('Home'); @@ -79,8 +81,10 @@ export const postPinnedList = .post(url + `?pins=${payload}`) .then(response => { dispatch(getAllCases()); + addClickstreamEvent(type === 'REMOVED' ? CLICKSTREAM_EVENT_NAMES.AV_CASE_LIST_REMOVE_API_SUCCESS : CLICKSTREAM_EVENT_NAMES.AV_TODO_LIST_SUBMIT_SUCCESS, {todoList: pinnedCases}) }) .catch(err => { + addClickstreamEvent(type === 'REMOVED' ? CLICKSTREAM_EVENT_NAMES.AV_CASE_LIST_REMOVE_API_FAILED : CLICKSTREAM_EVENT_NAMES.AV_TODO_LIST_SUBMIT_FAILED, {todoList: pinnedCases}) dispatch(setTodoListOffline(updatedCaseList)); }); }; @@ -89,6 +93,7 @@ export const syncCaseDetail = (data: any) => (dispatch: AppDispatch) => { const offlineImageIdList = getOfflineImageId(data); dispatch(updateCaseDetailAfterApiCall({caseId: data.id})) const url = getApiUrl(ApiKeys.FEEDBACK); + const apiTime = (new Date()).getTime(); axiosInstance .post(url, { id: data.id, diff --git a/src/common/Constants.ts b/src/common/Constants.ts index 49e349f6..b408e3b7 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -1,5 +1,8 @@ +import { ApiKeys, API_URLS } from "../components/utlis/apiHelper"; import { CaseStatuses } from "../screens/allCases/interface"; +export const APP_NAME = 'collections-agent-app'; + export enum CONDITIONAL_OPERATORS { EQUALS = 'EQUALS', LESS_THAN_EQUAL_TO = 'LESS_THAN_EQUAL_TO', @@ -27,6 +30,105 @@ export enum FirestoreUpdateTypes { REMOVED = 'removed', } +export const ClickstreamAPIToMonitor = { + [API_URLS[ApiKeys.GENERATE_OTP]]: 'AV_LOGIN_SCREEN_SEND_OTP_API', + [API_URLS[ApiKeys.VERIFY_OTP]]: 'AV_OTP_SCREEN_VERIFY_OTP_API', + [API_URLS[ApiKeys.LOGOUT]]: 'AV_PROFILE_PAGE_LOGOUT_API', + [API_URLS[ApiKeys.FEEDBACK]]: 'AV_FORM_SUBMIT_API' +}; + +export const CLICKSTREAM_EVENT_NAMES = { + // GENERIC + AV_NATIVE_BACK_PRESSED : {name: 'AV_NATIVE_BACK_PRESSED', description: 'Native back button pressed'}, + AV_NATIVE_HOME_PRESSED : {name: 'AV_NATIVE_HOME_PRESSED', description: 'Native home button pressed'}, + AV_APP_FOREGROUND : {name: 'AV_APP_FOREGROUND', description: 'App has come to foreground'}, + AV_APP_BACKGROUND : {name: 'AV_APP_BACKGROUND', description: 'App went to background'}, + AV_ERROR_PAGE_LOADED : {name: 'ERROR_PAGE_LOADED', description: 'Error page loaded'}, + + // Login screen + AV_LOGIN_SCREEN_LOAD: {name:'AV_LOGIN_SCREEN_LOAD', description: 'Login page loaded'}, + AV_LOGIN_SCREEN_NUMBER_INPUT: {name:'AV_LOGIN_SCREEN_NUMBER_INPUT', description: 'User entered 10 digits'}, + AV_LOGIN_SCREEN_SEND_OTP_CLICKED: {name:'AV_LOGIN_SCREEN_SEND_OTP_CLICKED', description: 'Send OTP CTA clicked'}, + AV_LOGIN_SCREEN_SEND_OTP_API_SUCCESS: {name:'AV_LOGIN_SCREEN_SEND_OTP_API_SUCCESS', description: 'Send OTP API successful'}, + AV_LOGIN_SCREEN_SEND_OTP_API_FAILED: {name:'AV_LOGIN_SCREEN_SEND_OTP_API_FAILED', description: 'Send OTP API failed'}, + + // OTP screen + AV_OTP_SCREEN_LOAD: {name:'AV_OTP_SCREEN_LOAD', description: 'OTP screen loaded'}, + AV_OTP_SCREEN_OTP_INPUT: {name:'AV_OTP_SCREEN_OTP_INPUT', description: 'User entered 4 digit OTP'}, + AV_OTP_SCREEN_VERIFY_OTP_CLICKED: {name:'AV_OTP_SCREEN_VERIFY_OTP_CLICKED', description: 'Verify OTP CTA Clicked'}, + AV_OTP_SCREEN_BACK_BUTTON_CLICKED: {name:'AV_OTP_SCREEN_BACK_BUTTON_CLICKED', description: 'OTP Screen back CTA clicked'}, + AV_OTP_SCREEN_VERIFY_OTP_API_SUCCESS: {name:'AV_OTP_SCREEN_VERIFY_OTP_API_SUCCESS', description: 'OTP verify API successful'}, + AV_OTP_SCREEN_VERIFY_OTP_API_FAILED: {name:'AV_OTP_SCREEN_VERIFY_OTP_API_FAILED', description: 'OTP verify API failed'}, + + // Case list + AV_CASE_LIST_LOAD : {name: 'AV_CASE_LIST_LOAD', description: 'Cases list loaded'}, + AV_CASE_LIST_CASE_CLICKED : {name: 'AV_CASE_LIST_CASE_CLICKED', description: 'Case clicked'}, + AV_CASE_LIST_PULLED_TO_REFRESH : {name: 'AV_CASE_LIST_PULLED_TO_REFRESH', description: 'Case list pulled to refresh'}, + AV_CASE_LIST_TODO_ITEM_SELECTED : {name: 'AV_CASE_LIST_TODO_ITEM_SELECTED', description: 'Case list todo item selected'}, + AV_CASE_LIST_TODO_ITEM_UNSELECTED : {name: 'AV_CASE_LIST_TODO_ITEM_UNSELECTED', description: 'Case list todo iten unselected'}, + AV_CASE_LIST_CASE_SELECTED : {name: 'AV_CASE_LIST_CASE_SELECTED', description: 'Other cases selected'}, + AV_CASE_LIST_CASE_UNSELECTED : {name: 'AV_CASE_LIST_CASE_UNSELECTED', description: 'Other cases unselected'}, + AV_CASE_LIST_PROCEED_BUTTON_CLICKED : {name: 'AV_CASE_LIST_PROCEED_BUTTON_CLICKED', description: 'Case list proceed CTA clicked'}, + AV_CASE_LIST_REMOVE_BUTTON_CLICKED : {name: 'AV_CASE_LIST_REMOVE_BUTTON_CLICKED', description: 'Case list remove CTA clicked'}, + AV_CASE_LIST_REMOVE_API_SUCCESS : {name: 'AV_CASE_LIST_REMOVE_API_SUCCESS', description: 'Remove todolist API successful'}, + AV_CASE_LIST_REMOVE_API_FAILED: {name: 'AV_CASE_LIST_REMOVE_API_FAILED', description: 'Remove todolist API failed'}, + AV_CASE_LIST_FILTER_BUTTON_CLICKED : {name: 'AV_CASE_LIST_FILTER_BUTTON_CLICKED', description: 'Case list filter CTA clicked'}, + AV_CASE_LIST_SEARCH_QUERY_CHANGED : {name: 'AV_CASE_LIST_SEARCH_QUERY_CHANGED', description: 'Search filter query changed'}, + AV_CASE_LIST_TAB_CHANGED : {name: 'AV_CASE_LIST_TAB_CHANGED', description: 'Case list tab changed'}, + AV_CASE_LIST_AGENT_PROFILE_CLICKED : {name: 'AV_CASE_LIST_AGENT_PROFILE_CLICKED', description: 'Case list agent profile CTA clicked'}, + + // TODO LIST + AV_TODO_LIST_LOAD : {name: 'AV_TODO_LIST_LOAD', description: 'Todo list confirmation screen loaded'}, + AV_TODO_LIST_CASE_DELETED : {name: 'AV_TODO_LIST_CASE_DELETED', description: 'Todo list case deleted'}, + AV_TODO_LIST_SUBMIT_CLICKED : {name: 'AV_TODO_LIST_SUBMIT_CLICKED', description: 'Submit CTA clicked'}, + AV_TODO_LIST_SUBMIT_SUCCESS : {name: 'AV_TODO_LIST_SUBMIT_SUCCESS', description: 'Todo list submit API successful'}, + AV_TODO_LIST_SUBMIT_FAILED : {name: 'AV_TODO_LIST_SUBMIT_FAILED', description: 'Todo list submit API failed'}, + AV_TODO_LIST_BACK_CLICKED : {name: 'AV_TODO_LIST_BACK_CLICKED', description: 'Back CTA clicked'}, + + //Filters + AV_FILTERS_PAGE_LOAD : {name: 'AV_FILTERS_PAGE_LOAD', description: 'Filters screen loaded'}, + AV_FILTERS_TAB_CLICKED : {name: 'AV_FILTERS_TAB_CLICKED', description: 'Filter tab clicked'}, + AV_FILTERS_OPTIONS_SELECTED : {name: 'AV_FILTERS_OPTIONS_SELECTED', description: 'Filter options selected'}, + AV_FILTERS_CLOSE_CLICKED : {name: 'AV_FILTERS_CLOSE_CLICKED', description: 'Filters close CTA clicked'}, + AV_FILTERS_APPLY_CLICKED : {name: 'AV_FILTERS_APPLY_CLICKED', description: 'Filters apply CTA clicked'}, + AV_FILTERS_CLEAR_CLICKED : {name: 'AV_FILTERS_CLEAR_CLICKED', description: 'Filters clear CTA clicked'}, + AV_FILTERS_OPTION_SEARCH : {name: 'AV_FILTERS_OPTION_SEARCH', description: 'Filters options search query'}, + + //Case Details + AV_CASE_DETAILS_PAGE_LOADED : {name: 'AV_CASE_DETAILS_PAGE_LOADED', description: 'Case details page loaded'}, + AV_CASE_DETAILS_PULL_TO_REFRESH : {name: 'AV_CASE_DETAILS_PULL_TO_REFRESH', description: 'Case details page pulled to refresh'}, + AV_CASE_DETAILS_CALL_CUSTOMER_CLICKED : {name: 'AV_CASE_DETAILS_CALL_CUSTOMER_CLICKED', description: 'Call customer CTA clicked'}, + AV_CASE_DETAILS_OPEN_MAPS_CLICKED : {name: 'AV_CASE_DETAILS_OPEN_MAPS_CLICKED', description: 'Open map CTA clicked'}, + AV_CASE_DETAILS_ADD_FEEDBACK_CLICKED : {name: 'AV_CASE_DETAILS_ADD_FEEDBACK_CLICKED', description: 'Add feedback CTA clicked'}, + + //CALLING BOTTOM SHEET + AV_CALLING_BOTTOMSHEET_LOADED : {name: 'AV_CALLING_BOTTOMSHEET_LOADED', description: 'Calling bottomsheet loaded'}, + AV_CALLING_BOTTOMSHEET_CLOSE_CLICKED : {name: 'AV_CALLING_BOTTOMSHEET_CLOSE_CLICKED' , description: 'Close bottomsheet CTA clicked'}, + AV_CALLING_BOTTOMSHEET_CALL_VIA_PHONE_CLICKED : {name: 'AV_CALLING_BOTTOMSHEET_CALL_VIA_PHONE_CLICKED', description: 'Call via phone CTA clicked'}, + AV_CALLING_BOTTOMSHEET_CALL_VIA_WHATSAPP_CLICKED : {name: 'AV_CALLING_BOTTOMSHEET_CALL_VIA_WHATSAPP_CLICKED', description: 'Call via whatsapp CTA clicked'}, + AV_CALLING_BOTTOMSHEET_BEFORE_INTENT_EVENT : {name: 'AV_CALLING_BOTTOMSHEET_BEFORE_INTENT_EVENT' , description: 'Call bottomsheet before intent event'}, + + //PROFILE PAGE + AV_PROFILE_PAGE_LOADED : {name: 'AV_PROFILE_PAGE_LOADED', description: 'Profile page loaded'}, + AV_PROFILE_PAGE_LOGOUT_BUTTON_CLICKED : {name: 'AV_PROFILE_PAGE_LOGOUT_BUTTON_CLICKED', description: 'Logout CTA clicked'}, + AV_PROFILE_PAGE_LOGOUT_CONFIRMATION_OPEN : {name: 'AV_PROFILE_PAGE_LOGOUT_CONFIRMATION_OPEN', description: 'Logout confirmation dialog open'}, + AV_PROFILE_PAGE_LOGOUT_CONFIRMATION_CLOSED : {name: 'AV_PROFILE_PAGE_LOGOUT_CONFIRMATION_CLOSED', description: 'Logout confirmation dialog closed'}, + AV_PROFILE_PAGE_LOGOUT_CONFIRMATION_CLICKED : {name: 'AV_PROFILE_PAGE_LOGOUT_CONFIRMATION_CLICKED', description: 'Logout confirmation clicked'}, + AV_PROFILE_PAGE_LOGOUT_API_SUCCESS : {name: 'AV_PROFILE_PAGE_LOGOUT_API_SUCCESS', description: 'Logout API successful'}, + AV_PROFILE_PAGE_LOGOUT_API_FAILED : {name: 'AV_PROFILE_PAGE_LOGOUT_API_FAILED', description: 'Logout API failed'}, + + //FORMS + AV_FORM_LOADED : {name: 'AV_FORM_LOADED', description: 'Form loaded'}, + AV_FORM_CLOSE_CLICKED : {name: 'AV_FORM_CLOSE_CLICKED', description: 'Form close CTA clicked'}, + AV_FORM_ELEMENT_CHANGED : {name: 'AV_FORM_ELEMENT_CHANGED', description: 'Form element answer changed'}, + AV_FORM_NEXT_BUTTON_CLICKED : {name: 'AV_FORM_NEXT_BUTTON_CLICKED', description: 'Form next CTA clicked'}, + AV_FORM_BACK_BUTTON_CLICKED : {name: 'AV_FORM_BACK_BUTTON_CLICKED', description: 'Form back CTA clicked'}, + AV_FORM_SUMMARY_PAGE_LOADED : {name: 'AV_FORM_SUMMARY_PAGE_LOADED', description: 'Form summary page loaded'}, + AV_FORM_SUBMIT_BUTTON_CLICKED : {name: 'AV_FORM_SUBMIT_BUTTON_CLICKED', description: 'Form submit journey CTA clicked'}, + AV_FORM_SUBMIT_API_SUCCESS : {name: 'AV_FORM_SUBMIT_API_SUCCESS', description: 'Form submit API successful'}, + AV_FORM_SUBMIT_API_FAILED : {name: 'AV_FORM_SUBMIT_API_FAILED', description: 'Form submit API failed'}, +} as Record; + export enum MimeType { 'image/jpeg' = 'image/jpeg', 'image/png' = 'image/png' diff --git a/src/common/ErrorBoundary.tsx b/src/common/ErrorBoundary.tsx index 75cda8d7..453e7ade 100644 --- a/src/common/ErrorBoundary.tsx +++ b/src/common/ErrorBoundary.tsx @@ -1,7 +1,8 @@ import React, { Component, ReactNode } from 'react'; import { logError } from '../components/utlis/errorUtils'; -import { StyleSheet, Text, View } from 'react-native'; -import { COLORS } from '../../RN-UI-LIB/src/styles/colors'; +import { StyleSheet, View } from 'react-native'; +import {addClickstreamEvent} from "../services/clickstreamEventService"; +import {CLICKSTREAM_EVENT_NAMES} from "./Constants"; import ErrorImage from '../assets/images/Error'; import Heading from '../../RN-UI-LIB/src/components/Heading'; @@ -24,9 +25,12 @@ class ErrorBoundary extends Component { this.setState({ hasError: true }); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_ERROR_PAGE_LOADED , {error}); logError(error); } + + render() { if (this.state.hasError) { return ( @@ -58,4 +62,4 @@ const styles = StyleSheet.create({ } }); -export default ErrorBoundary; \ No newline at end of file +export default ErrorBoundary; diff --git a/src/components/form/Submit.tsx b/src/components/form/Submit.tsx index 620d79a8..b5edad9f 100644 --- a/src/components/form/Submit.tsx +++ b/src/components/form/Submit.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { SafeAreaView, ScrollView, @@ -7,9 +7,10 @@ import { } from 'react-native'; import Text from '../../../RN-UI-LIB/src/components/Text'; import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles'; +import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants'; import { useAppSelector } from '../../hooks'; import AnswerRender, { IAnswer } from './AnswerRender'; - +import { addClickstreamEvent } from '../../services/clickstreamEventService'; interface ISubmit { caseId: string; @@ -29,6 +30,10 @@ const Submit: React.FC = props => { const sections = templateData.sections; const questions = templateData.questions; + useEffect(() => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_SUMMARY_PAGE_LOADED, {journeyId: journey}) + }, []) + return ( diff --git a/src/components/form/components/CheckboxGroup.tsx b/src/components/form/components/CheckboxGroup.tsx index 6dc69c14..238f3f9f 100644 --- a/src/components/form/components/CheckboxGroup.tsx +++ b/src/components/form/components/CheckboxGroup.tsx @@ -13,6 +13,8 @@ import { AnswerType } from '../interface'; import QuestionRenderingEngine from '../QuestionRenderingEngine'; import ErrorMessage from './ErrorMessage'; import { _map } from '../../../../RN-UI-LIB/src/utlis/common'; +import { addClickstreamEvent } from '../../../services/clickstreamEventService'; +import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants'; interface ICheckBoxGroup { questionType: string; @@ -26,7 +28,7 @@ interface ICheckBoxGroup { } const CheckBoxGroup: React.FC = props => { - const {questionId, widgetId, journeyId, caseId, sectionId, error} = props; + const {questionId, widgetId, journeyId, caseId, sectionId, error, questionType} = props; const template = useAppSelector(state => state.case.templateData) const question = template.questions[questionId as keyof typeof template.questions]; @@ -65,6 +67,9 @@ const CheckBoxGroup: React.FC = props => { onChange: (...event: any[]) => void, ) => { const values = _map(change, (key) => key); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_ELEMENT_CHANGED, { + caseId, questionType, value: values, questionId, error, sectionId, widgetId + }) onChange({ answer: values, type: AnswerType.array, diff --git a/src/components/form/components/Dropdown.tsx b/src/components/form/components/Dropdown.tsx index a09db022..67bb876e 100644 --- a/src/components/form/components/Dropdown.tsx +++ b/src/components/form/components/Dropdown.tsx @@ -13,6 +13,8 @@ import QuestionRenderingEngine from '../QuestionRenderingEngine'; import ErrorMessage from './ErrorMessage'; import {AnswerType, Options} from '../interface'; import {useAppSelector} from '../../../hooks'; +import { addClickstreamEvent } from '../../../services/clickstreamEventService'; +import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants'; interface IDropDown { questionType: string; @@ -26,7 +28,7 @@ interface IDropDown { } const DropDown: React.FC = props => { - const {questionId, widgetId, journeyId, caseId, sectionId, error} = props; + const {questionId, widgetId, journeyId, caseId, sectionId, error, questionType} = props; const template = useAppSelector(state => state.case.templateData); const question = template.questions[questionId as keyof typeof template.questions]; @@ -61,6 +63,9 @@ const DropDown: React.FC = props => { change: string | null, onChange: (...event: any[]) => void, ) => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_ELEMENT_CHANGED, { + caseId, questionType, value: change, questionId, error, sectionId, widgetId + }) onChange({ answer: change, type: AnswerType.option, diff --git a/src/components/form/components/ImageUpload.tsx b/src/components/form/components/ImageUpload.tsx index 61cefd6f..cc41e416 100644 --- a/src/components/form/components/ImageUpload.tsx +++ b/src/components/form/components/ImageUpload.tsx @@ -20,7 +20,8 @@ import { AnswerType } from '../interface'; import ErrorMessage from './ErrorMessage'; import withObservables from '@nozbe/with-observables'; import OfflineImageDAO from '../../../wmDB/dao/OfflineImageDAO'; -import { PrefixJpegBase64Image } from '../../../common/Constants'; +import { CLICKSTREAM_EVENT_NAMES, PrefixJpegBase64Image } from '../../../common/Constants'; +import { addClickstreamEvent } from '../../../services/clickstreamEventService'; interface IOfflineImage { idx: string; @@ -40,7 +41,7 @@ interface IImageUpload { } const ImageUpload: React.FC = props => { - const {questionId, error, sectionId, caseId, journeyId, widgetId} = props; + const {questionId, error, sectionId, caseId, journeyId, widgetId, questionType} = props; const [imageId, setImageId] = useState(''); const template = useAppSelector(state => state.case.templateData); const question = @@ -67,6 +68,9 @@ const ImageUpload: React.FC = props => { const data = `${PrefixJpegBase64Image}${clickedImage}`; var uniqueId = 'id' + (new Date()).getTime(); setImageId(uniqueId); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_ELEMENT_CHANGED, { + caseId, questionType, value: uniqueId, questionId, error, sectionId, widgetId + }) onChange({ answer: uniqueId, type: AnswerType.image, diff --git a/src/components/form/components/RadioButton.tsx b/src/components/form/components/RadioButton.tsx index 7352ade2..51c37fcc 100644 --- a/src/components/form/components/RadioButton.tsx +++ b/src/components/form/components/RadioButton.tsx @@ -15,6 +15,8 @@ import ErrorMessage from './ErrorMessage'; import {AnswerType} from '../interface'; import {useAppSelector} from '../../../hooks'; import Address from './Address'; +import { addClickstreamEvent } from '../../../services/clickstreamEventService'; +import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants'; interface IRadioButton { questionType: string; @@ -32,7 +34,7 @@ const AdditionalDetailsComponentMap = { }; const RadioButton: React.FC = props => { - const {questionId, widgetId, journeyId, caseId, sectionId, error} = props; + const {questionId, widgetId, journeyId, caseId, sectionId, error, questionType} = props; const template = useAppSelector(state => state.case.templateData); const question = template.questions[questionId]; const options = template.options; @@ -66,6 +68,9 @@ const RadioButton: React.FC = props => { change: string | null, onChange: (...event: any[]) => void, ) => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_ELEMENT_CHANGED, { + caseId, questionType, value: change, questionId, error, sectionId, widgetId + }) computeNextQuestion(change as keyof typeof options); onChange({ answer: change, diff --git a/src/components/form/components/Rating.tsx b/src/components/form/components/Rating.tsx index 568e9f2a..09c891e5 100644 --- a/src/components/form/components/Rating.tsx +++ b/src/components/form/components/Rating.tsx @@ -7,6 +7,8 @@ import ErrorMessage from './ErrorMessage'; import Text from '../../../../RN-UI-LIB/src/components/Text'; import { AnswerType } from '../interface'; import { useAppSelector } from '../../../hooks'; +import { addClickstreamEvent } from '../../../services/clickstreamEventService'; +import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants'; interface IRating { questionType: string; questionId: string; @@ -19,7 +21,7 @@ interface IRating { } const Rating: React.FC = props => { - const {questionId, error, sectionId, widgetId} = props; + const {questionId, error, sectionId, widgetId, caseId, questionType} = props; const template = useAppSelector(state => state.case.templateData); const question = template.questions[questionId as keyof typeof template.questions]; @@ -28,6 +30,9 @@ const Rating: React.FC = props => { } const handleChange = (text : number, onChange : (...event: any[]) => void) => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_ELEMENT_CHANGED, { + caseId, questionType, value: text, questionId, error, sectionId, widgetId + }) onChange({ answer: text, type: AnswerType.number diff --git a/src/components/form/components/TextArea.tsx b/src/components/form/components/TextArea.tsx index 44139df9..3cb4ee9d 100644 --- a/src/components/form/components/TextArea.tsx +++ b/src/components/form/components/TextArea.tsx @@ -8,6 +8,8 @@ import {GenericStyles} from '../../../../RN-UI-LIB/src/styles'; import ErrorMessage from './ErrorMessage'; import { AnswerType } from '../interface'; import { useAppSelector } from '../../../hooks'; +import { addClickstreamEvent } from '../../../services/clickstreamEventService'; +import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants'; interface ITextArea { questionType: string; @@ -21,7 +23,7 @@ interface ITextArea { } const TextArea: React.FC = props => { - const {questionId, error, sectionId, widgetId} = props; + const {questionId, error, sectionId, widgetId, caseId, questionType} = props; const template = useAppSelector(state => state.case.templateData); const question = template.questions[questionId as keyof typeof template.questions]; @@ -30,6 +32,9 @@ const TextArea: React.FC = props => { } const handleChange = (text : string, onChange : (...event: any[]) => void) => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_ELEMENT_CHANGED, { + caseId, questionType, value: text, questionId, error, sectionId, widgetId + }) onChange({ answer: text, type: AnswerType.text diff --git a/src/components/form/components/TextInput.tsx b/src/components/form/components/TextInput.tsx index 396d4dbf..9bee0045 100644 --- a/src/components/form/components/TextInput.tsx +++ b/src/components/form/components/TextInput.tsx @@ -8,6 +8,8 @@ import Text from '../../../../RN-UI-LIB/src/components/Text'; import ErrorMessage from './ErrorMessage'; import { AnswerType } from '../interface'; import { useAppSelector } from '../../../hooks'; +import { addClickstreamEvent } from '../../../services/clickstreamEventService'; +import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants'; interface ITextInput { questionType: string; @@ -21,7 +23,7 @@ interface ITextInput { } const TextInput: React.FC = props => { - const {questionId, error, sectionId, widgetId} = props; + const {questionId, error, sectionId, widgetId, caseId, questionType} = props; const template = useAppSelector(state => state.case.templateData); const question = template.questions[questionId as keyof typeof template.questions]; @@ -30,6 +32,9 @@ const TextInput: React.FC = props => { } const handleChange = (text : string, onChange : (...event: any[]) => void) => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_ELEMENT_CHANGED, { + caseId, questionType, value: text, questionId, error, sectionId, widgetId + }) onChange({ answer: text, type: AnswerType.text diff --git a/src/components/form/index.tsx b/src/components/form/index.tsx index f0336d39..e4349242 100644 --- a/src/components/form/index.tsx +++ b/src/components/form/index.tsx @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {useForm} from 'react-hook-form'; import {ScrollView, StyleSheet, TouchableHighlight, TouchableOpacity, View} from 'react-native'; import {SafeAreaView} from 'react-native-safe-area-context'; @@ -29,6 +29,8 @@ import {ConditionType, IDecision, ILeaf} from './interface'; import RenderQuestion from './RenderQuestion'; import {TaskTitleUIMapping} from '../../screens/allCases/interface'; import Submit from './Submit'; +import {addClickstreamEvent} from "../../services/clickstreamEventService"; +import {CLICKSTREAM_EVENT_NAMES} from "../../common/Constants"; const CASE_DETAIL_SCREEN_NAME = 'caseDetail'; @@ -50,6 +52,8 @@ const Widget: React.FC = props => { previousScreenRoute.name === CASE_DETAIL_SCREEN_NAME, ); + useEffect(()=>{},[]) + const {name, params} = props.route; const {caseId, journey} = params; const templateData = useAppSelector(state => state.case.templateData); @@ -215,6 +219,46 @@ const Widget: React.FC = props => { }); }; + useEffect(() => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_LOADED , { + caseId, + journeyId: journey, + widgetId: name, + sections: sections.map(section => ({section , questions: sectionMap[section].questions})), + }) + } , []) + + const handleCloseIconPress = () => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_CLOSE_CLICKED , {caseId}); + navigateToScreen('caseDetail', {caseId}) + } + + const handleSubmitCTA = () => { + if(isLeaf) { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_SUBMIT_BUTTON_CLICKED, {caseId, journeyId: journey, + widgetId: name}); + handleSubmit(handleSubmitJourney, onError) + } + else{ + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_NEXT_BUTTON_CLICKED, {caseId, journeyId: journey, + widgetId: name}); + handleSubmit(onSubmit, onError) + } + } + + const handleBackButton = () => { + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FORM_BACK_BUTTON_CLICKED, {caseId, journeyId: journey, + widgetId: name}); + dispatch( + deleteInteraction({ + caseId, + journeyId: journey, + widgetId: name, + }), + ); + goBack(); + } + return ( @@ -226,7 +270,7 @@ const Widget: React.FC = props => { paddingHorizontal: 16, }}> - navigateToScreen('caseDetail', {caseId})}> + @@ -305,27 +349,14 @@ const Widget: React.FC = props => { style={[styles.autoFlex, styles.mH16]} title={'Back'} testID={"test_back"} - onPress={() => { - dispatch( - deleteInteraction({ - caseId, - journeyId: journey, - widgetId: name, - }), - ); - goBack(); - }} + onPress={handleBackButton} leftIcon={} /> )}