From ad7ee95df8d8bd75e6c9613e13a152359cbad534 Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Thu, 9 Nov 2023 13:41:50 +0530 Subject: [PATCH] TP-46224 | houston clickstream impl (#87) * TP-46224 | initial setup done * TP-46224 | one event added * TP-46224 | more events added * TP-46224 | landding page events added * TP-46224 | review comments resolved * TP-46224 | testing * TP-46224 | useRef removed * TP-46224 | janus link added * TP-46224 | comment resolved --- entrypoint.sh | 2 +- setup.template.js | 1 + src/Pages/Dashboard/index.tsx | 11 ++++++ src/Pages/Dashboard/partials/Date.tsx | 9 +++++ .../Dashboard/partials/SearchResultsTable.tsx | 7 ++++ src/Pages/Tableau/index.tsx | 13 ++++++- src/Pages/Team/index.tsx | 13 ++++++- src/Pages/Team/partials/TeamForm.tsx | 20 +++++++---- src/services/clickStream/api/janusApi.ts | 16 +++++++++ src/services/clickStream/constants/index.ts | 31 +++++++++++++++++ src/services/clickStream/constants/values.ts | 23 +++++++++++++ src/services/clickStream/events.ts | 23 +++++++++++++ src/services/clickStream/index.ts | 23 +++++++++++++ src/services/clickStream/payload.ts | 34 +++++++++++++++++++ src/services/clickStream/types/index.d.ts | 34 +++++++++++++++++++ src/services/constants.ts | 1 + src/types/index.d.ts | 1 + 17 files changed, 252 insertions(+), 10 deletions(-) create mode 100644 src/services/clickStream/api/janusApi.ts create mode 100644 src/services/clickStream/constants/index.ts create mode 100644 src/services/clickStream/constants/values.ts create mode 100644 src/services/clickStream/events.ts create mode 100644 src/services/clickStream/index.ts create mode 100644 src/services/clickStream/payload.ts create mode 100644 src/services/clickStream/types/index.d.ts create mode 100644 src/services/constants.ts diff --git a/entrypoint.sh b/entrypoint.sh index bce8812..e332f94 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -7,6 +7,6 @@ sed -i "s~~${BASE_API_URL}~g" /usr/share/nginx/html/config.js sed -i "s~~${DISABLE_UNIVERSAL_AUTH}~g" /usr/share/nginx/html/config.js sed -i "s~CSP_HEADER~${CSP_HEADER}~g" /etc/nginx/conf.d/nginx.conf sed -i "s~~${TABLEAU_SERVER_URL}~g" /usr/share/nginx/html/config.js - +sed -i "s~~${JANUS_API_URL}~g" /usr/share/nginx/html/config.js exec "$@" diff --git a/setup.template.js b/setup.template.js index 751ec5d..02187b2 100644 --- a/setup.template.js +++ b/setup.template.js @@ -6,4 +6,5 @@ window.config = { CSP_HEADER: '', DISABLE_UNIVERSAL_AUTH: '', TABLEAU_SERVER_URL: '', + JANUS_API_URL: '', }; diff --git a/src/Pages/Dashboard/index.tsx b/src/Pages/Dashboard/index.tsx index b7517b7..5fd201f 100644 --- a/src/Pages/Dashboard/index.tsx +++ b/src/Pages/Dashboard/index.tsx @@ -4,6 +4,8 @@ import { toast } from '@navi/web-ui/lib/primitives/Toast/index'; import FallbackComponent from '@src/components/Fallback'; import { ApiService } from '@src/services/api'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; import { FETCH_INCIDENTS_DATA } from './constants'; import SearchResultsTable from './partials/SearchResultsTable'; import DashboardHeader from './partials/DashboardHeader'; @@ -26,6 +28,9 @@ const Dashboard: FC = () => { ); const searchParamRef = useRef(''); + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; + const startIncidentSearch = (param = pageNumberRef.current): void => { const endPoint = FETCH_INCIDENTS_DATA(param); setIsLoading(true); @@ -48,6 +53,12 @@ const Dashboard: FC = () => { }); }; + useEffect(() => { + fireEvent(EVENT_NAME.Houston_dashboard_Land, { + screen_name: SCREEN_NAME.DASHBOARD_PAGE, + }); + }, []); + useEffect(() => { startIncidentSearch(); return () => { diff --git a/src/Pages/Dashboard/partials/Date.tsx b/src/Pages/Dashboard/partials/Date.tsx index 2c1ace6..0c2ef2d 100644 --- a/src/Pages/Dashboard/partials/Date.tsx +++ b/src/Pages/Dashboard/partials/Date.tsx @@ -5,6 +5,8 @@ import { Dayjs } from 'dayjs'; import dayjs from 'dayjs'; import styles from './DashboardHeader.module.scss'; import { Button } from '@navi/web-ui/lib/primitives'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; export interface DateProps { setSelectedDate: (param: Array) => void; @@ -16,6 +18,10 @@ const Date: React.FC = (props: DateProps) => { const { fetchIncidentData } = props; const [date, setDate] = useState([]); const [open, setOpen] = useState(false); + + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; + const disabledDate = (current: Dayjs) => { if (current != null) { return current && current > dayjs().endOf('day'); @@ -73,6 +79,9 @@ const Date: React.FC = (props: DateProps) => { }; const handleApplyClick = () => { + fireEvent(EVENT_NAME.Houston_Check_DateFilter, { + screen_name: SCREEN_NAME.DASHBOARD_PAGE, + }); if (date.length === 2) { const start_date = date[0]; const end_date = date[1]; diff --git a/src/Pages/Dashboard/partials/SearchResultsTable.tsx b/src/Pages/Dashboard/partials/SearchResultsTable.tsx index 3792490..6a17656 100644 --- a/src/Pages/Dashboard/partials/SearchResultsTable.tsx +++ b/src/Pages/Dashboard/partials/SearchResultsTable.tsx @@ -4,6 +4,8 @@ import { AgTable, Pagination } from '@navi/web-ui/lib/components'; import { Tag, Drawer } from '@navi/web-ui/lib/primitives'; import { returnFormattedDate } from '@src/services/globalUtils'; import DrawerMode from '@src/Pages/Incidents/DrawerMode/index'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; import styles from '../SearchResultsTable.module.scss'; import { getSeverityColor } from '../constants'; import cx from 'classnames'; @@ -35,6 +37,8 @@ const SearchResultsTable: FC = ({ const [drawerState, setDrawerState] = useState(false); const incidentData = useRef({}); const navigate = useNavigate(); + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; const cellStyle = { 'text-overflow': 'ellipsis', @@ -132,6 +136,9 @@ const SearchResultsTable: FC = ({ }; const handleRowClick = (event: any) => { + fireEvent(EVENT_NAME.Houston_Check_Incident, { + screen_name: SCREEN_NAME.DASHBOARD_PAGE, + }); if (event?.data) { navigate(`/incident/${event.data?.id}`); } diff --git a/src/Pages/Tableau/index.tsx b/src/Pages/Tableau/index.tsx index 77b7c0a..0600f5d 100644 --- a/src/Pages/Tableau/index.tsx +++ b/src/Pages/Tableau/index.tsx @@ -1,10 +1,21 @@ -import { FC } from 'react'; +import { FC, useRef, useEffect } from 'react'; import Typography from '@navi/web-ui/lib/primitives/Typography'; import { TableauEmbed } from '@stoddabr/react-tableau-embed-live'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; import { TABLEAU_SDK_VERSION, TABLEAU_URL } from './constants'; import styles from './Tableau.module.scss'; const Tableau: FC = () => { + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; + + useEffect(() => { + fireEvent(EVENT_NAME.Houston_Metric_Land, { + screen_name: SCREEN_NAME.METRIC_PAGE, + }); + }, []); + return (
diff --git a/src/Pages/Team/index.tsx b/src/Pages/Team/index.tsx index 3953b11..7806d5a 100644 --- a/src/Pages/Team/index.tsx +++ b/src/Pages/Team/index.tsx @@ -1,8 +1,10 @@ -import { FC, useEffect, useState } from 'react'; +import { FC, useEffect, useState, useRef } from 'react'; import Typography from '@navi/web-ui/lib/primitives/Typography'; import { toast } from '@navi/web-ui/lib/primitives/Toast'; import FallbackComponent from '@src/components/Fallback'; import { ApiService } from '@src/services/api'; +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; import { FETCH_TEAM_DATA } from './constants'; import TeamResultsTable from './partials/TeamResultsTable'; import CreateTeam from './partials/CreateTeam'; @@ -26,6 +28,9 @@ const Team: FC = () => { ); const Role = useAuthData(); + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; + const startTeamSearch = (createdTeamId?: number): void => { const endPoint = FETCH_TEAM_DATA; dispatch(setIsLoading(true)); @@ -62,6 +67,12 @@ const Team: FC = () => { fetchAllBots(); }, []); + useEffect(() => { + fireEvent(EVENT_NAME.Houston_Team_Land, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); + }, []); + const createHandler = () => { dispatch(setModalOpen(true)); }; diff --git a/src/Pages/Team/partials/TeamForm.tsx b/src/Pages/Team/partials/TeamForm.tsx index e740132..989f02d 100644 --- a/src/Pages/Team/partials/TeamForm.tsx +++ b/src/Pages/Team/partials/TeamForm.tsx @@ -9,13 +9,8 @@ import { Typography, } from '@navi/web-ui/lib/primitives'; import { InfoIcon } from '@navi/web-ui/lib/icons'; - -import { - FETCH_SINGLE_TEAM_DATA, - TeamFormProps, - UPDATE_TEAM_DATA, -} from '../constants'; - +import useClickStream from '@src/services/clickStream'; +import { CLICK_STREAM_EVENT_FACTORY } from '@src/services/clickStream/constants/values'; import { ApiService } from '@src/services/api'; import MembersDetails from '@src/components/MembersDetails'; import DrawerStyles from '@src/Pages/Incidents/DrawerMode/DrawerMode.module.scss'; @@ -23,6 +18,11 @@ import styles from '../Team.module.scss'; import { ConfigProvider, Select, ThemeConfig } from 'antd'; import { getBots } from '../bots'; import SlackIcon from '@src/assets/SlackIcon'; +import { + FETCH_SINGLE_TEAM_DATA, + TeamFormProps, + UPDATE_TEAM_DATA, +} from '../constants'; const TeamForm = (props: TeamFormProps) => { const { teamId, isExpanded, setLastUpdatedAt } = props; @@ -35,6 +35,9 @@ const TeamForm = (props: TeamFormProps) => { const [psecOncallHandle, setPsecOncallHandle] = useState(''); const [showTooltip, setShowTooltip] = useState(false); + const { fireEvent } = useClickStream(); + const { EVENT_NAME, SCREEN_NAME } = CLICK_STREAM_EVENT_FACTORY; + const fetchTeamById = (updateDate = false, showMainLoader = true) => { const endPoint = FETCH_SINGLE_TEAM_DATA(teamId); if (showMainLoader) setIsLoading(true); @@ -65,6 +68,9 @@ const TeamForm = (props: TeamFormProps) => { }, [teamId, isExpanded]); const addMemberHandler = (): void => { + fireEvent(EVENT_NAME.Houston_Add_Member, { + screen_name: SCREEN_NAME.TEAM_PAGE, + }); const endPoint = UPDATE_TEAM_DATA(); const finalSlackData = slackUserEmails?.includes(',') ? slackUserEmails.split(',').map(item => item?.trim()) diff --git a/src/services/clickStream/api/janusApi.ts b/src/services/clickStream/api/janusApi.ts new file mode 100644 index 0000000..f62bcd0 --- /dev/null +++ b/src/services/clickStream/api/janusApi.ts @@ -0,0 +1,16 @@ +import { ApiService } from '../../api'; +import { ClickStreamPayload } from '../types'; +import { JANUS_API } from '@src/services/constants'; + +const useJanusApi = (): { + fireClickStreamPayload: (payload) => Promise; +} => { + const janusServiceUrl = `${JANUS_API}`; + + const fireClickStreamPayload = (payload: ClickStreamPayload): Promise => + ApiService.post(janusServiceUrl, { ...payload }); + + return { fireClickStreamPayload }; +}; + +export default useJanusApi; diff --git a/src/services/clickStream/constants/index.ts b/src/services/clickStream/constants/index.ts new file mode 100644 index 0000000..0de2901 --- /dev/null +++ b/src/services/clickStream/constants/index.ts @@ -0,0 +1,31 @@ +import { useMemo } from 'react'; + +import { BasePayload } from '../types'; +import { appName, eventsSource, eventBufferKey } from './values'; + +export { eventBufferKey }; + +const useConstants = (): { basePayload: BasePayload } => { + const { userAgent, vendor } = window.navigator; + const storedEmail = localStorage.getItem('email-id'); + + const basePayload = useMemo(() => { + return { + app: { + name: appName, + }, + device: { + model: userAgent, + manufacturer: vendor, + }, + source: eventsSource, + user: { + customer_id: storedEmail || 'unknown', + }, + }; + }, [userAgent, vendor]); + + return { basePayload }; +}; + +export default useConstants; diff --git a/src/services/clickStream/constants/values.ts b/src/services/clickStream/constants/values.ts new file mode 100644 index 0000000..c4e8fa5 --- /dev/null +++ b/src/services/clickStream/constants/values.ts @@ -0,0 +1,23 @@ +export const appName = 'houston-portal'; +export const eventsSource = 'HoustonPortal'; +export const eventBufferKey = 'clickStreamEvents'; + +const EVENT_NAME = { + Houston_Team_Land: 'Houston_Team_Land', + Houston_dashboard_Land: 'Houston_dashboard_Land', + Houston_Metric_Land: 'Houston_Metric_Land', + Houston_Add_Member: 'Houston_Add_Member', + Houston_Check_DateFilter: 'Houston_Check_DateFilter', + Houston_Check_Incident: 'Houston_Check_Incident', +}; + +const SCREEN_NAME = { + TEAM_PAGE: 'team_page', + DASHBOARD_PAGE: 'dashboard_page', + METRIC_PAGE: 'metric_page', +}; + +export const CLICK_STREAM_EVENT_FACTORY = { + EVENT_NAME, + SCREEN_NAME, +}; diff --git a/src/services/clickStream/events.ts b/src/services/clickStream/events.ts new file mode 100644 index 0000000..2fbbc31 --- /dev/null +++ b/src/services/clickStream/events.ts @@ -0,0 +1,23 @@ +import { Timestamp, ClickStreamEvent } from './types'; + +const useEvents = (): { + makeEvent: (...args: any) => any; +} => { + const makeEvent = ( + eventName: string, + timestamp: Timestamp, + attributes: Record, + ): ClickStreamEvent[] => [ + { + event_name: eventName, + timestamp, + attributes, + }, + ]; + + return { + makeEvent, + }; +}; + +export default useEvents; diff --git a/src/services/clickStream/index.ts b/src/services/clickStream/index.ts new file mode 100644 index 0000000..b538c8e --- /dev/null +++ b/src/services/clickStream/index.ts @@ -0,0 +1,23 @@ +import useJanusApi from './api/janusApi'; +import useEvents from './events'; +import usePayload from './payload'; + +const useClickStream = (): { + fireEvent: (event: string, attributes: Record) => void; +} => { + const { makeEvent } = useEvents(); + const { createPayload } = usePayload(); + const { fireClickStreamPayload } = useJanusApi(); + + const fireEvent = ( + event: string, + attributes: Record, + ): void => { + const finalEvent = makeEvent(event, Date.now(), attributes); + fireClickStreamPayload(createPayload(finalEvent)); + }; + + return { fireEvent }; +}; + +export default useClickStream; diff --git a/src/services/clickStream/payload.ts b/src/services/clickStream/payload.ts new file mode 100644 index 0000000..cfd3086 --- /dev/null +++ b/src/services/clickStream/payload.ts @@ -0,0 +1,34 @@ +import { + BasePayload, + ClickStreamEvent, + ClickStreamPayload, + EventsPayload, +} from './types'; +import useConstants from './constants'; + +const usePayload = (): { + createPayload: (events: ClickStreamEvent[]) => ClickStreamPayload; +} => { + const { basePayload } = useConstants(); + + const makePayload = ( + basePayload: BasePayload, + eventsPayload: EventsPayload, + ): ClickStreamPayload => ({ + ...basePayload, + ...eventsPayload, + }); + + const createPayload = (events: ClickStreamEvent[]): ClickStreamPayload => { + const timestamp = Date.now(); + const eventsPayload = { + events, + client_ts: timestamp, + }; + return makePayload(basePayload, eventsPayload); + }; + + return { createPayload }; +}; + +export default usePayload; diff --git a/src/services/clickStream/types/index.d.ts b/src/services/clickStream/types/index.d.ts new file mode 100644 index 0000000..68fb8d9 --- /dev/null +++ b/src/services/clickStream/types/index.d.ts @@ -0,0 +1,34 @@ +export type Timestamp = number; + +export interface AppInfo { + name: string; +} + +export interface DeviceInfo { + model: string; + manufacturer: string; +} + +export interface ClickStreamEvent { + event_name: string; + timestamp: Timestamp; + attributes?: Record; +} + +export interface UserInfo { + customer_id: string | unknown; +} + +export interface BasePayload { + app: AppInfo; + device: DeviceInfo; + source: string; + user: UserInfo; +} + +export interface EventsPayload { + client_ts: Timestamp; + events: ClickStreamEvent[]; +} + +export type ClickStreamPayload = BasePayload & EventsPayload; diff --git a/src/services/constants.ts b/src/services/constants.ts new file mode 100644 index 0000000..6cb014c --- /dev/null +++ b/src/services/constants.ts @@ -0,0 +1 @@ +export const JANUS_API = `${window?.config?.JANUS_API_URL}/events/json`; diff --git a/src/types/index.d.ts b/src/types/index.d.ts index e9b238b..d634852 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -8,6 +8,7 @@ interface AppConfig { CSP_HEADER: string; DISABLE_UNIVERSAL_AUTH: string; TABLEAU_SERVER_URL: string; + JANUS_API_URL: string; } declare global {