From df18512dbb0bcdfe5d7fca9ae6948f7cc9b0120b Mon Sep 17 00:00:00 2001 From: eylonmiz Date: Tue, 1 Aug 2023 21:17:47 +0300 Subject: [PATCH 1/7] feat(frontend-analytics): init - by userid --- .../prompts/views/PromptEditView.tsx | 11 ++- .../src/app/lib/providers/AuthProvider.tsx | 4 + apps/console/src/app/lib/utils/analytics.ts | 59 ++++++++++++ apps/console/src/app/lib/utils/sign-out.ts | 2 + apps/console/src/env.ts | 1 + apps/console/src/index.d.ts | 91 +++++++++++++++++++ apps/console/src/index.html | 5 + 7 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 apps/console/src/app/lib/utils/analytics.ts create mode 100644 apps/console/src/index.d.ts diff --git a/apps/console/src/app/components/prompts/views/PromptEditView.tsx b/apps/console/src/app/components/prompts/views/PromptEditView.tsx index a87bbd4c..d9d9f4dc 100644 --- a/apps/console/src/app/components/prompts/views/PromptEditView.tsx +++ b/apps/console/src/app/components/prompts/views/PromptEditView.tsx @@ -30,6 +30,7 @@ import { FunctionsFormModal } from "../FormModal"; import { InlineCodeSnippet } from "../../common/InlineCodeSnippet"; import { colors } from "../../../lib/theme/colors"; import { ConsumePromptModal } from "../ConsumePromptModal"; +import { trackEvent } from "../../../lib/utils/analytics"; const FUNCTIONS_FEATURE_FLAG = true; @@ -44,6 +45,11 @@ export const PromptEditView = () => { const [isPublishModalOpen, setIsPublishModalOpen] = useState(false); const [isFunctionsModalOpen, setIsFunctionsModalOpen] = useState(false); + const onConsumeClick = () => { + setIsConsumePromptModalOpen(true); + trackEvent("how_to_consume_prompt"); + }; + return ( !isPromptLoading && ( <> @@ -56,10 +62,7 @@ export const PromptEditView = () => { > {isPublishEnabled && ( - )} diff --git a/apps/console/src/app/lib/providers/AuthProvider.tsx b/apps/console/src/app/lib/providers/AuthProvider.tsx index 542a795c..a9ab9879 100644 --- a/apps/console/src/app/lib/providers/AuthProvider.tsx +++ b/apps/console/src/app/lib/providers/AuthProvider.tsx @@ -8,6 +8,7 @@ import { LayoutWrapper } from "../../components/layout/LayoutWrapper"; import { Loading3QuartersOutlined } from "@ant-design/icons"; import { colors } from "../theme/colors"; import { Loader } from "../../components/common/Loader"; +import { useIdentify, useTrackInit } from "../utils/analytics"; const SpinnerOverlay = styled(Row)` height: 100%; @@ -48,6 +49,9 @@ export const AuthProvider = ({ children }) => { } }, [value.currentUser]); + useTrackInit(value.currentUser?.id); + useIdentify(value.currentUser); + return ( {isLoading || isError || !data ? ( diff --git a/apps/console/src/app/lib/utils/analytics.ts b/apps/console/src/app/lib/utils/analytics.ts new file mode 100644 index 00000000..96b7cb6a --- /dev/null +++ b/apps/console/src/app/lib/utils/analytics.ts @@ -0,0 +1,59 @@ +import React from "react"; +import { GetMeQuery } from "../../../@generated/graphql/graphql"; +import { SEGMENT_WRITE_KEY } from "../../../env"; + +const shouldTrack = !!SEGMENT_WRITE_KEY; +if (!shouldTrack) { + console.log("Segment analytics disabled"); + window.analytics = { + identify: (...args: never) => null, + group: (...args: never) => null, + track: (...args: never) => null, + load: (...args: never) => null, + page: (...args: never) => null, + screen: (...args: never) => null, + alias: (...args: never) => null, + flush: (...args: never) => null, + }; +} + +export const useTrackInit = (userId: string) => { + React.useEffect(() => { + if (!window.analytics) return; + (window.analytics as any)._writeKey = SEGMENT_WRITE_KEY; + window.analytics.load(SEGMENT_WRITE_KEY); + }, []); + + React.useEffect(() => { + if (!userId) return; + window.analytics.identify(userId); + }, [userId]); +}; + +// Can be handled on backend +export const useIdentify = (user: GetMeQuery["me"]) => { + React.useEffect(() => { + if (!user) return; + // const orgId = user.organizationIds[0]; + const identifyRequest = { + userId: user.id, + name: user.name, + email: user.email, + avatar: user.photoUrl, + // company: { id: orgId }, + }; + + window.analytics.identify(identifyRequest); + // window.analytics.group(orgId); + }, [user]); +}; + +export type AnalyticsEvent = { + login: "login"; + logout: "logout"; + how_to_consume_prompt: "how_to_consume_prompt"; +}; + +export const trackEvent = (event: keyof AnalyticsEvent, properties?: any) => { + window.analytics.track({ event, properties }); +}; diff --git a/apps/console/src/app/lib/utils/sign-out.ts b/apps/console/src/app/lib/utils/sign-out.ts index 84615394..31963cb5 100644 --- a/apps/console/src/app/lib/utils/sign-out.ts +++ b/apps/console/src/app/lib/utils/sign-out.ts @@ -1,6 +1,8 @@ import { signOut as supertokensSignOut } from "supertokens-auth-react/recipe/session"; +import { trackEvent } from "./analytics"; export async function signOut() { + trackEvent("logout"); await supertokensSignOut(); localStorage.removeItem("currentOrgId"); window.location.href = "/login"; diff --git a/apps/console/src/env.ts b/apps/console/src/env.ts index 52594421..bef7a624 100644 --- a/apps/console/src/env.ts +++ b/apps/console/src/env.ts @@ -15,3 +15,4 @@ export const SUPERTOKENS_WEBSITE_DOMAIN = getEnvVariable( "NX_SUPERTOKENS_WEBSITE_DOMAIN" ); export const SENTRY_DSN_URL = getEnvVariable("NX_SENTRY_DSN_URL"); +export const SEGMENT_WRITE_KEY = getEnvVariable("NX_SEGMENT_WRITE_KEY"); diff --git a/apps/console/src/index.d.ts b/apps/console/src/index.d.ts new file mode 100644 index 00000000..cc05769a --- /dev/null +++ b/apps/console/src/index.d.ts @@ -0,0 +1,91 @@ +export {}; + +declare global { + class Analytics { + load(writeKey: string); + + /* The identify method lets you tie a user to their actions and record + traits about them. */ + identify( + message: Identity & { + traits?: any; + timestamp?: Date | undefined; + context?: any; + integrations?: Integrations | undefined; + }, + callback?: (err: Error) => void + ): Analytics; + + /* The track method lets you record the actions your users perform. */ + track( + message: Identity & { + event: string; + properties?: any; + timestamp?: Date | undefined; + context?: any; + integrations?: Integrations | undefined; + }, + callback?: (err: Error) => void + ): Analytics; + + /* The page method lets you record page views on your website, along with + optional extra information about the page being viewed. */ + page( + message: Identity & { + category?: string | undefined; + name?: string | undefined; + properties?: any; + timestamp?: Date | undefined; + context?: any; + integrations?: Integrations | undefined; + messageId?: string | undefined; + }, + callback?: (err: Error) => void + ): Analytics; + + /* The screen method lets you record whenever a user sees a screen, + the mobile equivalent of page, in your mobile app, along with + any properties about the screen. */ + screen( + message: Identity & { + name?: string | undefined; + properties?: any; + timestamp?: Date | undefined; + context?: any; + integrations?: Integrations | undefined; + }, + callback?: (err: Error) => void + ): Analytics; + + /* alias is how you associate one identity with another. */ + alias( + message: Identity & { + previousId: string | number; + integrations?: Integrations | undefined; + }, + callback?: (err: Error) => void + ): Analytics; + + /* Group calls can be used to associate individual users with shared + accounts or companies. */ + group( + message: Identity & { + groupId: string | number; + traits?: any; + context?: any; + timestamp?: Date | undefined; + integrations?: Integrations | undefined; + }, + callback?: (err: Error) => void + ): Analytics; + + /* Flush batched calls to make sure nothing is left in the queue */ + flush( + callback?: (err: Error, data: Data) => void + ): Promise<{ batch: any; timestamp: string; sentAt: string }>; + } + + interface Window { + analytics: Analytics; + } +} diff --git a/apps/console/src/index.html b/apps/console/src/index.html index 21922174..65514b9d 100644 --- a/apps/console/src/index.html +++ b/apps/console/src/index.html @@ -24,6 +24,11 @@ sizes="16x16" href="./assets/favicon/favicon-16x16.png" /> +
From a56ce41ccc0c7040e548d79261a47fb6c0ed2911 Mon Sep 17 00:00:00 2001 From: eylonmiz Date: Wed, 2 Aug 2023 16:08:03 +0300 Subject: [PATCH 2/7] feat(frontend-analytics): init - with groupId by organizationId --- .gitignore | 5 +- apps/console/src/app/lib/utils/analytics.ts | 25 +++++----- apps/console/src/app/lib/utils/event.types.ts | 5 ++ apps/console/src/index.d.ts | 49 ++----------------- 4 files changed, 24 insertions(+), 60 deletions(-) create mode 100644 apps/console/src/app/lib/utils/event.types.ts diff --git a/.gitignore b/.gitignore index 7f2072ec..701959cf 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,7 @@ Thumbs.db .nx-container .env.local -schema.graphql \ No newline at end of file +schema.graphql + +# playground +playground \ No newline at end of file diff --git a/apps/console/src/app/lib/utils/analytics.ts b/apps/console/src/app/lib/utils/analytics.ts index 96b7cb6a..3066d528 100644 --- a/apps/console/src/app/lib/utils/analytics.ts +++ b/apps/console/src/app/lib/utils/analytics.ts @@ -1,6 +1,7 @@ import React from "react"; import { GetMeQuery } from "../../../@generated/graphql/graphql"; import { SEGMENT_WRITE_KEY } from "../../../env"; +import { AnalyticsEvent } from "./event.types"; const shouldTrack = !!SEGMENT_WRITE_KEY; if (!shouldTrack) { @@ -10,9 +11,6 @@ if (!shouldTrack) { group: (...args: never) => null, track: (...args: never) => null, load: (...args: never) => null, - page: (...args: never) => null, - screen: (...args: never) => null, - alias: (...args: never) => null, flush: (...args: never) => null, }; } @@ -34,26 +32,27 @@ export const useTrackInit = (userId: string) => { export const useIdentify = (user: GetMeQuery["me"]) => { React.useEffect(() => { if (!user) return; - // const orgId = user.organizationIds[0]; + const groupId = JSON.parse(localStorage.getItem("currentOrgId")); + const identifyRequest = { userId: user.id, name: user.name, email: user.email, avatar: user.photoUrl, - // company: { id: orgId }, + groupId, }; window.analytics.identify(identifyRequest); - // window.analytics.group(orgId); + window.analytics.group(groupId); }, [user]); }; -export type AnalyticsEvent = { - login: "login"; - logout: "logout"; - how_to_consume_prompt: "how_to_consume_prompt"; -}; - export const trackEvent = (event: keyof AnalyticsEvent, properties?: any) => { - window.analytics.track({ event, properties }); + const groupId = JSON.parse(localStorage.getItem("currentOrgId")); + const context = { groupId }; + window.analytics.track( + event, + { ...properties, organizationId: groupId }, + context + ); }; diff --git a/apps/console/src/app/lib/utils/event.types.ts b/apps/console/src/app/lib/utils/event.types.ts new file mode 100644 index 00000000..712451a1 --- /dev/null +++ b/apps/console/src/app/lib/utils/event.types.ts @@ -0,0 +1,5 @@ +export type AnalyticsEvent = { + login: "login"; + logout: "logout"; + how_to_consume_prompt: "how_to_consume_prompt"; +}; diff --git a/apps/console/src/index.d.ts b/apps/console/src/index.d.ts index cc05769a..067f54fa 100644 --- a/apps/console/src/index.d.ts +++ b/apps/console/src/index.d.ts @@ -18,52 +18,9 @@ declare global { /* The track method lets you record the actions your users perform. */ track( - message: Identity & { - event: string; - properties?: any; - timestamp?: Date | undefined; - context?: any; - integrations?: Integrations | undefined; - }, - callback?: (err: Error) => void - ): Analytics; - - /* The page method lets you record page views on your website, along with - optional extra information about the page being viewed. */ - page( - message: Identity & { - category?: string | undefined; - name?: string | undefined; - properties?: any; - timestamp?: Date | undefined; - context?: any; - integrations?: Integrations | undefined; - messageId?: string | undefined; - }, - callback?: (err: Error) => void - ): Analytics; - - /* The screen method lets you record whenever a user sees a screen, - the mobile equivalent of page, in your mobile app, along with - any properties about the screen. */ - screen( - message: Identity & { - name?: string | undefined; - properties?: any; - timestamp?: Date | undefined; - context?: any; - integrations?: Integrations | undefined; - }, - callback?: (err: Error) => void - ): Analytics; - - /* alias is how you associate one identity with another. */ - alias( - message: Identity & { - previousId: string | number; - integrations?: Integrations | undefined; - }, - callback?: (err: Error) => void + event: string, + properties: any, + context: { groupId: string; projectId?: string } ): Analytics; /* Group calls can be used to associate individual users with shared From 5cd092e92127aba72710f8fd169186fbaf9de2a4 Mon Sep 17 00:00:00 2001 From: eylonmiz Date: Wed, 2 Aug 2023 17:25:10 +0300 Subject: [PATCH 3/7] feat(frontend-analytics): format fix --- apps/console/src/app/lib/utils/analytics.ts | 5 +- apps/console/src/index.html | 63 ++++++++++++++++++++- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/apps/console/src/app/lib/utils/analytics.ts b/apps/console/src/app/lib/utils/analytics.ts index 3066d528..a383f467 100644 --- a/apps/console/src/app/lib/utils/analytics.ts +++ b/apps/console/src/app/lib/utils/analytics.ts @@ -47,7 +47,10 @@ export const useIdentify = (user: GetMeQuery["me"]) => { }, [user]); }; -export const trackEvent = (event: keyof AnalyticsEvent, properties?: any) => { +export const trackEvent = ( + event: keyof AnalyticsEvent, + properties?: Record +) => { const groupId = JSON.parse(localStorage.getItem("currentOrgId")); const context = { groupId }; window.analytics.track( diff --git a/apps/console/src/index.html b/apps/console/src/index.html index 65514b9d..7b758c47 100644 --- a/apps/console/src/index.html +++ b/apps/console/src/index.html @@ -25,9 +25,66 @@ href="./assets/favicon/favicon-16x16.png" /> From a9fd1e0739a029f2c40013c336d98e711ee94a0d Mon Sep 17 00:00:00 2001 From: eylonmiz Date: Wed, 2 Aug 2023 18:59:59 +0300 Subject: [PATCH 4/7] feat(frontend-analytics): analytics with analytics lib --- apps/console/src/app/lib/utils/analytics.ts | 43 +- apps/console/src/index.d.ts | 48 --- apps/console/src/index.html | 62 --- package-lock.json | 412 ++++++++++++++++++++ package.json | 2 + 5 files changed, 432 insertions(+), 135 deletions(-) delete mode 100644 apps/console/src/index.d.ts diff --git a/apps/console/src/app/lib/utils/analytics.ts b/apps/console/src/app/lib/utils/analytics.ts index a383f467..71f4c5e6 100644 --- a/apps/console/src/app/lib/utils/analytics.ts +++ b/apps/console/src/app/lib/utils/analytics.ts @@ -1,30 +1,27 @@ import React from "react"; +import Analytics from "analytics"; +import segmentPlugin from "@analytics/segment"; import { GetMeQuery } from "../../../@generated/graphql/graphql"; import { SEGMENT_WRITE_KEY } from "../../../env"; import { AnalyticsEvent } from "./event.types"; const shouldTrack = !!SEGMENT_WRITE_KEY; -if (!shouldTrack) { - console.log("Segment analytics disabled"); - window.analytics = { - identify: (...args: never) => null, - group: (...args: never) => null, - track: (...args: never) => null, - load: (...args: never) => null, - flush: (...args: never) => null, - }; -} -export const useTrackInit = (userId: string) => { - React.useEffect(() => { - if (!window.analytics) return; - (window.analytics as any)._writeKey = SEGMENT_WRITE_KEY; - window.analytics.load(SEGMENT_WRITE_KEY); - }, []); +const analytics = Analytics({ + app: "awesome-app", + plugins: shouldTrack + ? [ + segmentPlugin({ + writeKey: SEGMENT_WRITE_KEY, + }), + ] + : [], +}); +export const useTrackInit = (userId: string) => { React.useEffect(() => { if (!userId) return; - window.analytics.identify(userId); + analytics.identify(userId); }, [userId]); }; @@ -35,15 +32,15 @@ export const useIdentify = (user: GetMeQuery["me"]) => { const groupId = JSON.parse(localStorage.getItem("currentOrgId")); const identifyRequest = { - userId: user.id, name: user.name, email: user.email, avatar: user.photoUrl, groupId, }; - window.analytics.identify(identifyRequest); - window.analytics.group(groupId); + analytics.identify(user.id, identifyRequest); + // unsafe support for segment group in analytics lib + (analytics.plugins as any).segment?.group(groupId, {}); }, [user]); }; @@ -53,9 +50,5 @@ export const trackEvent = ( ) => { const groupId = JSON.parse(localStorage.getItem("currentOrgId")); const context = { groupId }; - window.analytics.track( - event, - { ...properties, organizationId: groupId }, - context - ); + analytics.track(event, { ...properties, organizationId: groupId }, context); }; diff --git a/apps/console/src/index.d.ts b/apps/console/src/index.d.ts deleted file mode 100644 index 067f54fa..00000000 --- a/apps/console/src/index.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -export {}; - -declare global { - class Analytics { - load(writeKey: string); - - /* The identify method lets you tie a user to their actions and record - traits about them. */ - identify( - message: Identity & { - traits?: any; - timestamp?: Date | undefined; - context?: any; - integrations?: Integrations | undefined; - }, - callback?: (err: Error) => void - ): Analytics; - - /* The track method lets you record the actions your users perform. */ - track( - event: string, - properties: any, - context: { groupId: string; projectId?: string } - ): Analytics; - - /* Group calls can be used to associate individual users with shared - accounts or companies. */ - group( - message: Identity & { - groupId: string | number; - traits?: any; - context?: any; - timestamp?: Date | undefined; - integrations?: Integrations | undefined; - }, - callback?: (err: Error) => void - ): Analytics; - - /* Flush batched calls to make sure nothing is left in the queue */ - flush( - callback?: (err: Error, data: Data) => void - ): Promise<{ batch: any; timestamp: string; sentAt: string }>; - } - - interface Window { - analytics: Analytics; - } -} diff --git a/apps/console/src/index.html b/apps/console/src/index.html index 7b758c47..21922174 100644 --- a/apps/console/src/index.html +++ b/apps/console/src/index.html @@ -24,68 +24,6 @@ sizes="16x16" href="./assets/favicon/favicon-16x16.png" /> -
diff --git a/package-lock.json b/package-lock.json index 6d496d98..60846eaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.4.0", "license": "MIT", "dependencies": { + "@analytics/segment": "^1.1.4", "@ant-design/icons": "^5.0.1", "@ant-design/icons-svg": "^4.2.1", "@apollo/server": "^4.7.0", @@ -45,6 +46,7 @@ "@uiw/codemirror-extensions-classname": "^4.21.3", "@uiw/codemirror-theme-material": "^4.19.16", "@uiw/react-codemirror": "^4.19.16", + "analytics": "^0.8.9", "antd": "^5.4.5", "apollo-server-errors": "^3.3.1", "apollo-server-express": "^3.12.0", @@ -195,6 +197,79 @@ "node": ">=6.0.0" } }, + "node_modules/@analytics/cookie-utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@analytics/cookie-utils/-/cookie-utils-0.2.12.tgz", + "integrity": "sha512-2h/yuIu3kmu+ZJlKmlT6GoRvUEY2k1BbQBezEv5kGhnn9KpmzPz715Y3GmM2i+m7Y0QmBdVUoA260dQZkofs2A==", + "dependencies": { + "@analytics/global-storage-utils": "^0.1.7" + } + }, + "node_modules/@analytics/core": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/@analytics/core/-/core-0.12.7.tgz", + "integrity": "sha512-etmIPCoxWLoUZ/o1o2zvIk4cdVHa8I1xUQtTuLA+YXQ4SsFbm75ZoMXJBqWrNSENpqCJgoL6hizl5uTbkNN+1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/davidwells" + } + ], + "dependencies": { + "@analytics/global-storage-utils": "^0.1.7", + "@analytics/type-utils": "^0.6.2", + "analytics-utils": "^1.0.12" + } + }, + "node_modules/@analytics/global-storage-utils": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@analytics/global-storage-utils/-/global-storage-utils-0.1.7.tgz", + "integrity": "sha512-V+spzGLZYm4biZT4uefaylm80SrLXf8WOTv9hCgA46cLcyxx3LD4GCpssp1lj+RcWLl/uXJQBRO4Mnn/o1x6Gw==", + "dependencies": { + "@analytics/type-utils": "^0.6.2" + } + }, + "node_modules/@analytics/localstorage-utils": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@analytics/localstorage-utils/-/localstorage-utils-0.1.10.tgz", + "integrity": "sha512-uJS+Jp1yLG5VFCgA5T82ZODYBS0xuDQx0NtAZrgbqt9j51BX3TcgmOez5LVkrUNu/lpbxjCLq35I4TKj78VmOQ==", + "dependencies": { + "@analytics/global-storage-utils": "^0.1.7" + } + }, + "node_modules/@analytics/segment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@analytics/segment/-/segment-1.1.4.tgz", + "integrity": "sha512-NL+2lY1+uxRa6iMwclvtyGm8HNc/4DDIczcIaqWSR+m5hsOUaA3iy0+yLRzduqnvOK0n1O5RLLJM5QulY5JScw==", + "dependencies": { + "analytics-node": "^3.5.0" + } + }, + "node_modules/@analytics/session-storage-utils": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@analytics/session-storage-utils/-/session-storage-utils-0.0.7.tgz", + "integrity": "sha512-PSv40UxG96HVcjY15e3zOqU2n8IqXnH8XvTkg1X43uXNTKVSebiI2kUjA3Q7ESFbw5DPwcLbJhV7GforpuBLDw==", + "dependencies": { + "@analytics/global-storage-utils": "^0.1.7" + } + }, + "node_modules/@analytics/storage-utils": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@analytics/storage-utils/-/storage-utils-0.4.2.tgz", + "integrity": "sha512-AXObwyVQw9h2uJh1t2hUgabtVxzYpW+7uKVbdHQK80vr3Td5rrmCxrCxarh7HUuAgSDZ0bZWqmYxVgmwKceaLg==", + "dependencies": { + "@analytics/cookie-utils": "^0.2.12", + "@analytics/global-storage-utils": "^0.1.7", + "@analytics/localstorage-utils": "^0.1.10", + "@analytics/session-storage-utils": "^0.0.7", + "@analytics/type-utils": "^0.6.2" + } + }, + "node_modules/@analytics/type-utils": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@analytics/type-utils/-/type-utils-0.6.2.tgz", + "integrity": "sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==" + }, "node_modules/@angular-devkit/core": { "version": "15.2.4", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.4.tgz", @@ -9202,6 +9277,15 @@ "ieee754": "^1.2.1" } }, + "node_modules/@segment/loosely-validate-event": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", + "integrity": "sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==", + "dependencies": { + "component-type": "^1.2.1", + "join-component": "^1.1.0" + } + }, "node_modules/@sendgrid/client": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-7.7.0.tgz", @@ -10346,6 +10430,12 @@ "@types/ms": "*" } }, + "node_modules/@types/dlv": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/dlv/-/dlv-1.1.2.tgz", + "integrity": "sha512-OyiZ3jEKu7RtGO1yp9oOdK0cTwZ/10oE9PDJ6fyN3r9T5wkyOcvr6awdugjYdqF6KVO5eUvt7jx7rk2Eylufow==", + "peer": true + }, "node_modules/@types/eslint": { "version": "8.37.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", @@ -11575,6 +11665,68 @@ "ajv": "^8.8.2" } }, + "node_modules/analytics": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/analytics/-/analytics-0.8.9.tgz", + "integrity": "sha512-oTbUzQpncMTslakqfK70GgB6bopk5hY+uuekwnadMkDyqNLgcD02KRzteTnO7q5Ko6wDECVtT8xi/6OuAMZykA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/davidwells" + } + ], + "dependencies": { + "@analytics/core": "^0.12.7", + "@analytics/storage-utils": "^0.4.2" + } + }, + "node_modules/analytics-node": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-3.5.0.tgz", + "integrity": "sha512-XgQq6ejZHCehUSnZS4V7QJPLIP7S9OAWwQDYl4WTLtsRvc5fCxIwzK/yihzmIW51v9PnyBmrl9dMcqvwfOE8WA==", + "dependencies": { + "@segment/loosely-validate-event": "^2.0.0", + "axios": "^0.21.1", + "axios-retry": "^3.0.2", + "lodash.isstring": "^4.0.1", + "md5": "^2.2.1", + "ms": "^2.0.0", + "remove-trailing-slash": "^0.1.0", + "uuid": "^3.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/analytics-node/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/analytics-node/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/analytics-utils": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/analytics-utils/-/analytics-utils-1.0.12.tgz", + "integrity": "sha512-WvV2YWgsnXLxaY0QYux0crpBAg/0JA763NmbMVz22jKhMPo7dpTBet8G2IlF7ixTjLDzGlkHk1ZaKqqQmjJ+4w==", + "dependencies": { + "@analytics/type-utils": "^0.6.2", + "dlv": "^1.1.3" + }, + "peerDependencies": { + "@types/dlv": "^1.0.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -12476,6 +12628,15 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-retry": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.6.0.tgz", + "integrity": "sha512-jtH4qWTKZ2a17dH6tjq52Y1ssNV0lKge6/Z9Lw67s9Wt01nGTg4hg7/LJBGYfDci44NTANJQlCPHPOT/TSFm9w==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "is-retry-allowed": "^2.2.0" + } + }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -13629,6 +13790,14 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -13957,6 +14126,11 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, + "node_modules/component-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.1.tgz", + "integrity": "sha512-Kgy+2+Uwr75vAi6ChWXgHuLvd+QLD7ssgpaRq2zCvt80ptvAfMc/hijcJxXkBa2wMlEZcJvC2H8Ubo+A9ATHIg==" + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -14321,6 +14495,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/css": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -18953,6 +19135,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -19233,6 +19420,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-set": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", @@ -23247,6 +23445,11 @@ "@sideway/pinpoint": "^2.0.0" } }, + "node_modules/join-component": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz", + "integrity": "sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==" + }, "node_modules/jose": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.1.tgz", @@ -23904,6 +24107,11 @@ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -24176,6 +24384,16 @@ "node": ">=0.10.0" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -27658,6 +27876,11 @@ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true }, + "node_modules/remove-trailing-slash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz", + "integrity": "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==" + }, "node_modules/remove-trailing-spaces": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", @@ -31624,6 +31847,73 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@analytics/cookie-utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@analytics/cookie-utils/-/cookie-utils-0.2.12.tgz", + "integrity": "sha512-2h/yuIu3kmu+ZJlKmlT6GoRvUEY2k1BbQBezEv5kGhnn9KpmzPz715Y3GmM2i+m7Y0QmBdVUoA260dQZkofs2A==", + "requires": { + "@analytics/global-storage-utils": "^0.1.7" + } + }, + "@analytics/core": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/@analytics/core/-/core-0.12.7.tgz", + "integrity": "sha512-etmIPCoxWLoUZ/o1o2zvIk4cdVHa8I1xUQtTuLA+YXQ4SsFbm75ZoMXJBqWrNSENpqCJgoL6hizl5uTbkNN+1Q==", + "requires": { + "@analytics/global-storage-utils": "^0.1.7", + "@analytics/type-utils": "^0.6.2", + "analytics-utils": "^1.0.12" + } + }, + "@analytics/global-storage-utils": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@analytics/global-storage-utils/-/global-storage-utils-0.1.7.tgz", + "integrity": "sha512-V+spzGLZYm4biZT4uefaylm80SrLXf8WOTv9hCgA46cLcyxx3LD4GCpssp1lj+RcWLl/uXJQBRO4Mnn/o1x6Gw==", + "requires": { + "@analytics/type-utils": "^0.6.2" + } + }, + "@analytics/localstorage-utils": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@analytics/localstorage-utils/-/localstorage-utils-0.1.10.tgz", + "integrity": "sha512-uJS+Jp1yLG5VFCgA5T82ZODYBS0xuDQx0NtAZrgbqt9j51BX3TcgmOez5LVkrUNu/lpbxjCLq35I4TKj78VmOQ==", + "requires": { + "@analytics/global-storage-utils": "^0.1.7" + } + }, + "@analytics/segment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@analytics/segment/-/segment-1.1.4.tgz", + "integrity": "sha512-NL+2lY1+uxRa6iMwclvtyGm8HNc/4DDIczcIaqWSR+m5hsOUaA3iy0+yLRzduqnvOK0n1O5RLLJM5QulY5JScw==", + "requires": { + "analytics-node": "^3.5.0" + } + }, + "@analytics/session-storage-utils": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@analytics/session-storage-utils/-/session-storage-utils-0.0.7.tgz", + "integrity": "sha512-PSv40UxG96HVcjY15e3zOqU2n8IqXnH8XvTkg1X43uXNTKVSebiI2kUjA3Q7ESFbw5DPwcLbJhV7GforpuBLDw==", + "requires": { + "@analytics/global-storage-utils": "^0.1.7" + } + }, + "@analytics/storage-utils": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@analytics/storage-utils/-/storage-utils-0.4.2.tgz", + "integrity": "sha512-AXObwyVQw9h2uJh1t2hUgabtVxzYpW+7uKVbdHQK80vr3Td5rrmCxrCxarh7HUuAgSDZ0bZWqmYxVgmwKceaLg==", + "requires": { + "@analytics/cookie-utils": "^0.2.12", + "@analytics/global-storage-utils": "^0.1.7", + "@analytics/localstorage-utils": "^0.1.10", + "@analytics/session-storage-utils": "^0.0.7", + "@analytics/type-utils": "^0.6.2" + } + }, + "@analytics/type-utils": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@analytics/type-utils/-/type-utils-0.6.2.tgz", + "integrity": "sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==" + }, "@angular-devkit/core": { "version": "15.2.4", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.4.tgz", @@ -38268,6 +38558,15 @@ } } }, + "@segment/loosely-validate-event": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz", + "integrity": "sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==", + "requires": { + "component-type": "^1.2.1", + "join-component": "^1.1.0" + } + }, "@sendgrid/client": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-7.7.0.tgz", @@ -39061,6 +39360,12 @@ "@types/ms": "*" } }, + "@types/dlv": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/dlv/-/dlv-1.1.2.tgz", + "integrity": "sha512-OyiZ3jEKu7RtGO1yp9oOdK0cTwZ/10oE9PDJ6fyN3r9T5wkyOcvr6awdugjYdqF6KVO5eUvt7jx7rk2Eylufow==", + "peer": true + }, "@types/eslint": { "version": "8.37.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", @@ -40078,6 +40383,54 @@ "fast-deep-equal": "^3.1.3" } }, + "analytics": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/analytics/-/analytics-0.8.9.tgz", + "integrity": "sha512-oTbUzQpncMTslakqfK70GgB6bopk5hY+uuekwnadMkDyqNLgcD02KRzteTnO7q5Ko6wDECVtT8xi/6OuAMZykA==", + "requires": { + "@analytics/core": "^0.12.7", + "@analytics/storage-utils": "^0.4.2" + } + }, + "analytics-node": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/analytics-node/-/analytics-node-3.5.0.tgz", + "integrity": "sha512-XgQq6ejZHCehUSnZS4V7QJPLIP7S9OAWwQDYl4WTLtsRvc5fCxIwzK/yihzmIW51v9PnyBmrl9dMcqvwfOE8WA==", + "requires": { + "@segment/loosely-validate-event": "^2.0.0", + "axios": "^0.21.1", + "axios-retry": "^3.0.2", + "lodash.isstring": "^4.0.1", + "md5": "^2.2.1", + "ms": "^2.0.0", + "remove-trailing-slash": "^0.1.0", + "uuid": "^3.2.1" + }, + "dependencies": { + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "analytics-utils": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/analytics-utils/-/analytics-utils-1.0.12.tgz", + "integrity": "sha512-WvV2YWgsnXLxaY0QYux0crpBAg/0JA763NmbMVz22jKhMPo7dpTBet8G2IlF7ixTjLDzGlkHk1ZaKqqQmjJ+4w==", + "requires": { + "@analytics/type-utils": "^0.6.2", + "dlv": "^1.1.3" + } + }, "ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -40742,6 +41095,15 @@ "proxy-from-env": "^1.1.0" } }, + "axios-retry": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.6.0.tgz", + "integrity": "sha512-jtH4qWTKZ2a17dH6tjq52Y1ssNV0lKge6/Z9Lw67s9Wt01nGTg4hg7/LJBGYfDci44NTANJQlCPHPOT/TSFm9w==", + "requires": { + "@babel/runtime": "^7.15.4", + "is-retry-allowed": "^2.2.0" + } + }, "axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -41641,6 +42003,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" + }, "check-more-types": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", @@ -41886,6 +42253,11 @@ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, + "component-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-type/-/component-type-1.2.1.tgz", + "integrity": "sha512-Kgy+2+Uwr75vAi6ChWXgHuLvd+QLD7ssgpaRq2zCvt80ptvAfMc/hijcJxXkBa2wMlEZcJvC2H8Ubo+A9ATHIg==" + }, "compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -42189,6 +42561,11 @@ "which": "^2.0.1" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" + }, "css": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -45657,6 +46034,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -45834,6 +46216,11 @@ "is-unc-path": "^1.0.0" } }, + "is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" + }, "is-set": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", @@ -48981,6 +49368,11 @@ "@sideway/pinpoint": "^2.0.0" } }, + "join-component": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz", + "integrity": "sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==" + }, "jose": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.1.tgz", @@ -49483,6 +49875,11 @@ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -49705,6 +50102,16 @@ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -52148,6 +52555,11 @@ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true }, + "remove-trailing-slash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz", + "integrity": "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==" + }, "remove-trailing-spaces": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", diff --git a/package.json b/package.json index 08dc1fcc..2ba34663 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ }, "private": true, "dependencies": { + "@analytics/segment": "^1.1.4", "@ant-design/icons": "^5.0.1", "@ant-design/icons-svg": "^4.2.1", "@apollo/server": "^4.7.0", @@ -45,6 +46,7 @@ "@uiw/codemirror-extensions-classname": "^4.21.3", "@uiw/codemirror-theme-material": "^4.19.16", "@uiw/react-codemirror": "^4.19.16", + "analytics": "^0.8.9", "antd": "^5.4.5", "apollo-server-errors": "^3.3.1", "apollo-server-express": "^3.12.0", From 9fbc61675c1b6c21533eb4a78b6a02d50899ec18 Mon Sep 17 00:00:00 2001 From: eylonmiz Date: Wed, 2 Aug 2023 19:26:23 +0300 Subject: [PATCH 5/7] feat(frontend-analytics): context props --- apps/console/src/app/lib/utils/analytics.ts | 25 ++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/console/src/app/lib/utils/analytics.ts b/apps/console/src/app/lib/utils/analytics.ts index 71f4c5e6..733b80de 100644 --- a/apps/console/src/app/lib/utils/analytics.ts +++ b/apps/console/src/app/lib/utils/analytics.ts @@ -44,11 +44,34 @@ export const useIdentify = (user: GetMeQuery["me"]) => { }, [user]); }; +interface ContextProps { + organizationId?: string; + projectId?: string; + promptId?: string; +} + +const getContextPropsFromPathIfExists = () => { + const contextProps: ContextProps = {}; + const path = window.location.pathname; + const [, projectsPath, projectId, promptsPath, promptId] = path.split("/"); + if (projectsPath === "projects" && projectId) { + contextProps.projectId = projectId; + } + if (promptsPath === "prompts" && promptId) { + contextProps.promptId = promptId; + } + return contextProps; +}; + export const trackEvent = ( event: keyof AnalyticsEvent, properties?: Record ) => { const groupId = JSON.parse(localStorage.getItem("currentOrgId")); const context = { groupId }; - analytics.track(event, { ...properties, organizationId: groupId }, context); + const contextProps = { + ...getContextPropsFromPathIfExists(), + organizationId: groupId, + }; + analytics.track(event, { ...contextProps, ...properties }, context); }; From 84febf9f88f6dac4a0260ee8c816d56bd4a794e1 Mon Sep 17 00:00:00 2001 From: eylonmiz Date: Wed, 2 Aug 2023 19:28:04 +0300 Subject: [PATCH 6/7] feat(frontend-analytics): temp folder --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 701959cf..8e7e15d6 100644 --- a/.gitignore +++ b/.gitignore @@ -47,5 +47,5 @@ Thumbs.db .env.local schema.graphql -# playground -playground \ No newline at end of file +# temp +temp \ No newline at end of file From 3fcab80ce8cdfca6a825c2cbae2653ebd8d9bfdc Mon Sep 17 00:00:00 2001 From: eylonmiz Date: Wed, 2 Aug 2023 19:56:12 +0300 Subject: [PATCH 7/7] feat(frontend-analytics): naming convention --- .../src/app/components/prompts/views/PromptEditView.tsx | 2 +- apps/console/src/app/lib/utils/analytics.ts | 2 +- apps/console/src/app/lib/utils/event.types.ts | 5 ----- apps/console/src/app/lib/utils/events.types.ts | 7 +++++++ 4 files changed, 9 insertions(+), 7 deletions(-) delete mode 100644 apps/console/src/app/lib/utils/event.types.ts create mode 100644 apps/console/src/app/lib/utils/events.types.ts diff --git a/apps/console/src/app/components/prompts/views/PromptEditView.tsx b/apps/console/src/app/components/prompts/views/PromptEditView.tsx index d9d9f4dc..4c3c5889 100644 --- a/apps/console/src/app/components/prompts/views/PromptEditView.tsx +++ b/apps/console/src/app/components/prompts/views/PromptEditView.tsx @@ -47,7 +47,7 @@ export const PromptEditView = () => { const onConsumeClick = () => { setIsConsumePromptModalOpen(true); - trackEvent("how_to_consume_prompt"); + trackEvent("how_to_consume_modal_open"); }; return ( diff --git a/apps/console/src/app/lib/utils/analytics.ts b/apps/console/src/app/lib/utils/analytics.ts index 733b80de..86394f1d 100644 --- a/apps/console/src/app/lib/utils/analytics.ts +++ b/apps/console/src/app/lib/utils/analytics.ts @@ -3,7 +3,7 @@ import Analytics from "analytics"; import segmentPlugin from "@analytics/segment"; import { GetMeQuery } from "../../../@generated/graphql/graphql"; import { SEGMENT_WRITE_KEY } from "../../../env"; -import { AnalyticsEvent } from "./event.types"; +import { AnalyticsEvent } from "./events.types"; const shouldTrack = !!SEGMENT_WRITE_KEY; diff --git a/apps/console/src/app/lib/utils/event.types.ts b/apps/console/src/app/lib/utils/event.types.ts deleted file mode 100644 index 712451a1..00000000 --- a/apps/console/src/app/lib/utils/event.types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type AnalyticsEvent = { - login: "login"; - logout: "logout"; - how_to_consume_prompt: "how_to_consume_prompt"; -}; diff --git a/apps/console/src/app/lib/utils/events.types.ts b/apps/console/src/app/lib/utils/events.types.ts new file mode 100644 index 00000000..15155aea --- /dev/null +++ b/apps/console/src/app/lib/utils/events.types.ts @@ -0,0 +1,7 @@ +// Pattern snake_case, Context => Object => Action + +export type AnalyticsEvent = { + login: "login"; + logout: "logout"; + how_to_consume_modal_open: "how_to_consume_modal_open"; +};