From 9cf5475134b7e9289df505e1c68a2b51be227eb1 Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Fri, 2 Aug 2024 19:30:31 +0530 Subject: [PATCH 1/2] added ms clarity script & consent for tracking --- src/@types/parseable/api/about.ts | 1 + src/api/constants.ts | 1 + src/components/Navbar/infoModal.tsx | 10 ++- src/hooks/useLoginForm.ts | 6 +- src/layouts/MainLayout/index.tsx | 32 +++++++-- .../MainLayout/providers/AppProvider.tsx | 9 ++- src/pages/Login/index.tsx | 33 ++++++++- src/pages/Login/styles/Login.module.css | 67 ++++++++++--------- 8 files changed, 113 insertions(+), 46 deletions(-) diff --git a/src/@types/parseable/api/about.ts b/src/@types/parseable/api/about.ts index 2149c362..43df60ab 100644 --- a/src/@types/parseable/api/about.ts +++ b/src/@types/parseable/api/about.ts @@ -14,4 +14,5 @@ export type AboutData = { grpcPort: number; oidcActive: boolean; cache: string; + sendAnalytics: boolean; }; diff --git a/src/api/constants.ts b/src/api/constants.ts index 00327e63..860dd993 100644 --- a/src/api/constants.ts +++ b/src/api/constants.ts @@ -42,3 +42,4 @@ export const CACHING_STATUS_URL = (streamName: string) => `${LOG_STREAM_LIST_URL export const CLUSTER_INFO_URL = `${API_V1}/cluster/info`; export const CLUSTER_METRICS_URL = `${API_V1}/cluster/metrics`; export const INGESTOR_DELETE_URL = (ingestorUrl: string) => `${API_V1}/cluster/${ingestorUrl}`; +export const CLARITY_TAG = 'ngxysymavo'; diff --git a/src/components/Navbar/infoModal.tsx b/src/components/Navbar/infoModal.tsx index 01ddd0df..6aa25eb7 100644 --- a/src/components/Navbar/infoModal.tsx +++ b/src/components/Navbar/infoModal.tsx @@ -16,7 +16,7 @@ const InfoModal: FC = (props) => { const { opened, close } = props; const { getAboutData, getAboutIsError, getAboutIsLoading } = useAbout(); - const [, setAppStore] = useAppStore((_store) => null); + const [allowClarityTracking, setAppStore] = useAppStore((store) => store.allowClarityTracking); const llmStatus = useMemo(() => { let status = 'LLM API Key not set'; if (getAboutData?.data?.llmActive) { @@ -114,7 +114,9 @@ const InfoModal: FC = (props) => { Store - {getAboutData?.data?.store?.type} + + {getAboutData?.data?.store?.type} + @@ -128,6 +130,10 @@ const InfoModal: FC = (props) => { LLM Status {llmStatus} + + Usage Analytics + {allowClarityTracking ? 'Tracking' : 'Not Tracking'} + ) : null} diff --git a/src/hooks/useLoginForm.ts b/src/hooks/useLoginForm.ts index 4ad9516a..31bdadf9 100644 --- a/src/hooks/useLoginForm.ts +++ b/src/hooks/useLoginForm.ts @@ -10,6 +10,7 @@ import { useId } from '@mantine/hooks'; import { useEffect } from 'react'; import Cookies from 'js-cookie'; import { getQueryParam } from '@/utils'; +import _ from 'lodash'; export const useLoginForm = () => { const notificationId = useId(); @@ -35,6 +36,7 @@ export const useLoginForm = () => { initialValues: { username: queryParams.username ?? '', password: queryParams.password ?? '', + allowClarityTracking: true, }, validate: { username: (value) => (value ? null : ''), @@ -44,6 +46,7 @@ export const useLoginForm = () => { return { username: values.username.trim(), password: values.password.trim(), + allowClarityTracking: values.allowClarityTracking, }; }, }); @@ -59,10 +62,11 @@ export const useLoginForm = () => { switch (res.status) { case StatusCodes.OK: { + localStorage.setItem('allowClarityTracking', _.toString(data.allowClarityTracking)); const pathname = location.state?.from?.pathname || HOME_ROUTE; nav( { - pathname + pathname, }, { replace: true }, ); diff --git a/src/layouts/MainLayout/index.tsx b/src/layouts/MainLayout/index.tsx index 66c500cf..5ea8c69b 100644 --- a/src/layouts/MainLayout/index.tsx +++ b/src/layouts/MainLayout/index.tsx @@ -2,23 +2,30 @@ import { PrimaryHeader } from '@/components/Header'; import Navbar from '@/components/Navbar'; import { NAVBAR_WIDTH, PRIMARY_HEADER_HEIGHT } from '@/constants/theme'; import { Box } from '@mantine/core'; -import { useCallback, useEffect, type FC } from 'react'; +import { useCallback, useEffect, useState, type FC } from 'react'; import { Outlet } from 'react-router-dom'; import { heights } from '@/components/Mantine/sizing'; import { useAppStore, appStoreReducers } from './providers/AppProvider'; +import _ from 'lodash'; +import { CLARITY_TAG } from '@/api/constants'; const { toggleMaximize } = appStoreReducers; const MainLayout: FC = () => { const [maximized, setAppStore] = useAppStore((store) => store.maximized); + const [allowClarityTracking] = useAppStore((store) => store.allowClarityTracking); + const [trackingScriptsAdded, setTrackingScriptsAdded] = useState(false); const primaryHeaderHeight = !maximized ? PRIMARY_HEADER_HEIGHT : 0; const navbarWidth = !maximized ? NAVBAR_WIDTH : 0; - const handleEscKeyPress = useCallback((event: KeyboardEvent) => { - if (event.key === 'Escape') { - maximized && setAppStore(toggleMaximize); - } - }, [maximized]); + const handleEscKeyPress = useCallback( + (event: KeyboardEvent) => { + if (event.key === 'Escape') { + maximized && setAppStore(toggleMaximize); + } + }, + [maximized], + ); useEffect(() => { window.addEventListener('keydown', handleEscKeyPress); @@ -27,6 +34,19 @@ const MainLayout: FC = () => { }; }, [maximized]); + useEffect(() => { + if (allowClarityTracking && _.isString(CLARITY_TAG) && !_.isEmpty(CLARITY_TAG) && !trackingScriptsAdded) { + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.innerHTML = `(function(c,l,a,r,i,t,y){ c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); })(window, document, "clarity", "script", "${CLARITY_TAG}");`; + document.body.appendChild(script); + setTrackingScriptsAdded(true); + return () => { + document.body.removeChild(script); + }; + } + }, [allowClarityTracking]); + return ( diff --git a/src/layouts/MainLayout/providers/AppProvider.tsx b/src/layouts/MainLayout/providers/AppProvider.tsx index 5dcc8019..37033a30 100644 --- a/src/layouts/MainLayout/providers/AppProvider.tsx +++ b/src/layouts/MainLayout/providers/AppProvider.tsx @@ -29,8 +29,9 @@ type AppStore = { streamSpecificUserAccess: string[] | null; instanceConfig: AboutData | null; isStandAloneMode: boolean | null; - savedFilters: SavedFilterType[] | null; // null to verify whether filters have been fetched or not + savedFilters: SavedFilterType[] | null; // null to verify whether filters have been fetched or not activeSavedFilters: SavedFilterType[]; // stream specific + allowClarityTracking: boolean; }; type AppStoreReducers = { @@ -59,6 +60,7 @@ const initialState: AppStore = { isStandAloneMode: null, savedFilters: null, activeSavedFilters: [], + allowClarityTracking: false, }; const { Provider: AppProvider, useStore: useAppStore } = initContext(initialState); @@ -122,8 +124,9 @@ const setStreamSpecificUserAccess = (store: AppStore) => { }; const setInstanceConfig = (_store: AppStore, instanceConfig: AboutData | null) => { - const { mode } = instanceConfig || {}; - return { instanceConfig, isStandAloneMode: mode === 'Standalone' }; + const { mode, sendAnalytics } = instanceConfig || {}; + const allowClarityTracking = !!(sendAnalytics && localStorage.getItem('allowClarityTracking') === 'true'); + return { instanceConfig, isStandAloneMode: mode === 'Standalone', allowClarityTracking }; }; const setSavedFilters = (store: AppStore, savedFiltersResponse: AxiosResponse) => { diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index 8ea3b5b3..02266e8f 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -1,10 +1,22 @@ import logo from '@/assets/images/brand/logo.svg'; import Loading from '@/components/Loading'; import { useLoginForm } from '@/hooks/useLoginForm'; -import { Box, Button, Divider, Image, PasswordInput, Text, TextInput, Transition, rem } from '@mantine/core'; +import { + Box, + Button, + Checkbox, + Divider, + Image, + PasswordInput, + Stack, + Text, + TextInput, + Transition, + rem, +} from '@mantine/core'; import { useDocumentTitle } from '@mantine/hooks'; import { FC } from 'react'; -import loginStyles from './styles/Login.module.css' +import loginStyles from './styles/Login.module.css'; const baseURL = import.meta.env.VITE_PARSEABLE_URL ?? '/'; const Login: FC = () => { @@ -15,6 +27,7 @@ const Login: FC = () => { const classes = loginStyles; const { container, formContainer, titleStyle, formInput, loginBtnStyle, errorStyle, sideContainer } = classes; + const allowTrackFieldProps = getInputProps('allowClarityTracking'); return ( { }}> Unified logs for all applications and infrastructure - Access, debug and analyze your log data here. Run sophisticated SQL queries on your log data, or look at tabular view. + Access, debug and analyze your log data here. Run sophisticated SQL queries on your log data, or look at + tabular view. @@ -96,6 +110,19 @@ const Login: FC = () => { })}> Login with OAuth + + + + + Parseable uses MS Clarity for detailed user interaction analytics in our console, driving targeted + enhancements to optimize the user experience. + + )} diff --git a/src/pages/Login/styles/Login.module.css b/src/pages/Login/styles/Login.module.css index 1a4a7759..2a8f205c 100644 --- a/src/pages/Login/styles/Login.module.css +++ b/src/pages/Login/styles/Login.module.css @@ -4,15 +4,15 @@ width: 30vw; background: radial-gradient(circle at 78.7% 87.8%, #FFFFFF 70%, #E1EAEE 60%); padding: 20px; - } - - @media (max-width: 768px) { +} + +@media (max-width: 768px) { .sideContainer { - display: none; + display: none; } - } - - .container { +} + +.container { position: relative; flex: 1; display: flex; @@ -20,50 +20,55 @@ justify-content: center; background-repeat: no-repeat; background-position: top center; - } - - .formContainer { +} + +.formContainer { position: relative; padding: 24px; border-radius: 4px; box-shadow: 0 0 24px 0 rgba(0, 0, 0, 0.2); border: 1px solid #D4D4D4; - width: 20rem; + width: 22rem; display: flex; flex-direction: column; align-items: center; - } - - .formInput { +} + +.formInput { width: 100%; - } - - .titleStyle { +} + +.titleStyle { color: #10143E; font-weight: 700; font-size: 24px; - } - - .descriptionStyle { +} + +.descriptionStyle { text-align: center; font-size: 14px; color: #828282; - } - - .errorStyle { +} + +.errorStyle { color: #FC466B; - } - - .loginBtnStyle { +} + +.loginBtnStyle { width: 100%; background-color: #545BEB; + &:hover { background-color: #FC466B; } - } +} - .loginBtnStyle:disabled, - .loginBtnStyle:disabled:hover - { +.loginBtnStyle:disabled, +.loginBtnStyle:disabled:hover { background-color: var(--mantine-color-gray-4); - } \ No newline at end of file +} + +.consentDescription { + font-size: 0.66rem; + color: var(--mantine-color-gray-6); +} \ No newline at end of file From d5ff02e8aed36a26bf10b76d50f5a1445c9b4c47 Mon Sep 17 00:00:00 2001 From: balaji-jr Date: Mon, 5 Aug 2024 17:18:56 +0530 Subject: [PATCH 2/2] changes for allowing user to add their own clarity tag --- src/@types/parseable/api/about.ts | 4 ++- src/api/constants.ts | 1 - src/components/Navbar/infoModal.tsx | 4 +-- src/hooks/useLoginForm.ts | 6 +--- src/layouts/MainLayout/index.tsx | 9 +++-- .../MainLayout/providers/AppProvider.tsx | 7 ++-- src/pages/Login/index.tsx | 33 ++----------------- 7 files changed, 15 insertions(+), 49 deletions(-) diff --git a/src/@types/parseable/api/about.ts b/src/@types/parseable/api/about.ts index 43df60ab..3f3adffe 100644 --- a/src/@types/parseable/api/about.ts +++ b/src/@types/parseable/api/about.ts @@ -14,5 +14,7 @@ export type AboutData = { grpcPort: number; oidcActive: boolean; cache: string; - sendAnalytics: boolean; + analytics: { + clarityTag: string; + } }; diff --git a/src/api/constants.ts b/src/api/constants.ts index 860dd993..00327e63 100644 --- a/src/api/constants.ts +++ b/src/api/constants.ts @@ -42,4 +42,3 @@ export const CACHING_STATUS_URL = (streamName: string) => `${LOG_STREAM_LIST_URL export const CLUSTER_INFO_URL = `${API_V1}/cluster/info`; export const CLUSTER_METRICS_URL = `${API_V1}/cluster/metrics`; export const INGESTOR_DELETE_URL = (ingestorUrl: string) => `${API_V1}/cluster/${ingestorUrl}`; -export const CLARITY_TAG = 'ngxysymavo'; diff --git a/src/components/Navbar/infoModal.tsx b/src/components/Navbar/infoModal.tsx index 6aa25eb7..4a752857 100644 --- a/src/components/Navbar/infoModal.tsx +++ b/src/components/Navbar/infoModal.tsx @@ -16,7 +16,7 @@ const InfoModal: FC = (props) => { const { opened, close } = props; const { getAboutData, getAboutIsError, getAboutIsLoading } = useAbout(); - const [allowClarityTracking, setAppStore] = useAppStore((store) => store.allowClarityTracking); + const [analytics, setAppStore] = useAppStore((store) => store.instanceConfig?.analytics); const llmStatus = useMemo(() => { let status = 'LLM API Key not set'; if (getAboutData?.data?.llmActive) { @@ -132,7 +132,7 @@ const InfoModal: FC = (props) => { Usage Analytics - {allowClarityTracking ? 'Tracking' : 'Not Tracking'} + {analytics?.clarityTag ? 'Tracking (MS Clarity)' : 'Not Tracking'} diff --git a/src/hooks/useLoginForm.ts b/src/hooks/useLoginForm.ts index 31bdadf9..4ad9516a 100644 --- a/src/hooks/useLoginForm.ts +++ b/src/hooks/useLoginForm.ts @@ -10,7 +10,6 @@ import { useId } from '@mantine/hooks'; import { useEffect } from 'react'; import Cookies from 'js-cookie'; import { getQueryParam } from '@/utils'; -import _ from 'lodash'; export const useLoginForm = () => { const notificationId = useId(); @@ -36,7 +35,6 @@ export const useLoginForm = () => { initialValues: { username: queryParams.username ?? '', password: queryParams.password ?? '', - allowClarityTracking: true, }, validate: { username: (value) => (value ? null : ''), @@ -46,7 +44,6 @@ export const useLoginForm = () => { return { username: values.username.trim(), password: values.password.trim(), - allowClarityTracking: values.allowClarityTracking, }; }, }); @@ -62,11 +59,10 @@ export const useLoginForm = () => { switch (res.status) { case StatusCodes.OK: { - localStorage.setItem('allowClarityTracking', _.toString(data.allowClarityTracking)); const pathname = location.state?.from?.pathname || HOME_ROUTE; nav( { - pathname, + pathname }, { replace: true }, ); diff --git a/src/layouts/MainLayout/index.tsx b/src/layouts/MainLayout/index.tsx index 5ea8c69b..4c5e498a 100644 --- a/src/layouts/MainLayout/index.tsx +++ b/src/layouts/MainLayout/index.tsx @@ -7,13 +7,12 @@ import { Outlet } from 'react-router-dom'; import { heights } from '@/components/Mantine/sizing'; import { useAppStore, appStoreReducers } from './providers/AppProvider'; import _ from 'lodash'; -import { CLARITY_TAG } from '@/api/constants'; const { toggleMaximize } = appStoreReducers; const MainLayout: FC = () => { const [maximized, setAppStore] = useAppStore((store) => store.maximized); - const [allowClarityTracking] = useAppStore((store) => store.allowClarityTracking); + const [analytics] = useAppStore((store) => store.instanceConfig?.analytics); const [trackingScriptsAdded, setTrackingScriptsAdded] = useState(false); const primaryHeaderHeight = !maximized ? PRIMARY_HEADER_HEIGHT : 0; const navbarWidth = !maximized ? NAVBAR_WIDTH : 0; @@ -35,17 +34,17 @@ const MainLayout: FC = () => { }, [maximized]); useEffect(() => { - if (allowClarityTracking && _.isString(CLARITY_TAG) && !_.isEmpty(CLARITY_TAG) && !trackingScriptsAdded) { + if (analytics && _.isString(analytics.clarityTag) && !_.isEmpty(analytics.clarityTag) && !trackingScriptsAdded) { const script = document.createElement('script'); script.type = 'text/javascript'; - script.innerHTML = `(function(c,l,a,r,i,t,y){ c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); })(window, document, "clarity", "script", "${CLARITY_TAG}");`; + script.innerHTML = `(function(c,l,a,r,i,t,y){ c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); })(window, document, "clarity", "script", "${analytics.clarityTag}");`; document.body.appendChild(script); setTrackingScriptsAdded(true); return () => { document.body.removeChild(script); }; } - }, [allowClarityTracking]); + }, [analytics]); return ( diff --git a/src/layouts/MainLayout/providers/AppProvider.tsx b/src/layouts/MainLayout/providers/AppProvider.tsx index 37033a30..d4a8b225 100644 --- a/src/layouts/MainLayout/providers/AppProvider.tsx +++ b/src/layouts/MainLayout/providers/AppProvider.tsx @@ -31,7 +31,6 @@ type AppStore = { isStandAloneMode: boolean | null; savedFilters: SavedFilterType[] | null; // null to verify whether filters have been fetched or not activeSavedFilters: SavedFilterType[]; // stream specific - allowClarityTracking: boolean; }; type AppStoreReducers = { @@ -60,7 +59,6 @@ const initialState: AppStore = { isStandAloneMode: null, savedFilters: null, activeSavedFilters: [], - allowClarityTracking: false, }; const { Provider: AppProvider, useStore: useAppStore } = initContext(initialState); @@ -124,9 +122,8 @@ const setStreamSpecificUserAccess = (store: AppStore) => { }; const setInstanceConfig = (_store: AppStore, instanceConfig: AboutData | null) => { - const { mode, sendAnalytics } = instanceConfig || {}; - const allowClarityTracking = !!(sendAnalytics && localStorage.getItem('allowClarityTracking') === 'true'); - return { instanceConfig, isStandAloneMode: mode === 'Standalone', allowClarityTracking }; + const { mode } = instanceConfig || {}; + return { instanceConfig, isStandAloneMode: mode === 'Standalone' }; }; const setSavedFilters = (store: AppStore, savedFiltersResponse: AxiosResponse) => { diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index 02266e8f..8ea3b5b3 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -1,22 +1,10 @@ import logo from '@/assets/images/brand/logo.svg'; import Loading from '@/components/Loading'; import { useLoginForm } from '@/hooks/useLoginForm'; -import { - Box, - Button, - Checkbox, - Divider, - Image, - PasswordInput, - Stack, - Text, - TextInput, - Transition, - rem, -} from '@mantine/core'; +import { Box, Button, Divider, Image, PasswordInput, Text, TextInput, Transition, rem } from '@mantine/core'; import { useDocumentTitle } from '@mantine/hooks'; import { FC } from 'react'; -import loginStyles from './styles/Login.module.css'; +import loginStyles from './styles/Login.module.css' const baseURL = import.meta.env.VITE_PARSEABLE_URL ?? '/'; const Login: FC = () => { @@ -27,7 +15,6 @@ const Login: FC = () => { const classes = loginStyles; const { container, formContainer, titleStyle, formInput, loginBtnStyle, errorStyle, sideContainer } = classes; - const allowTrackFieldProps = getInputProps('allowClarityTracking'); return ( { }}> Unified logs for all applications and infrastructure - Access, debug and analyze your log data here. Run sophisticated SQL queries on your log data, or look at - tabular view. + Access, debug and analyze your log data here. Run sophisticated SQL queries on your log data, or look at tabular view. @@ -110,19 +96,6 @@ const Login: FC = () => { })}> Login with OAuth - - - - - Parseable uses MS Clarity for detailed user interaction analytics in our console, driving targeted - enhancements to optimize the user experience. - - )}