@codeswayam/analytics 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -0
- package/package.json +23 -0
- package/src/Analytics.tsx +118 -0
- package/src/index.tsx +3 -0
- package/src/server.ts +22 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# @codeswayam/analytics
|
|
2
|
+
|
|
3
|
+
**Version:** 0.1.0
|
|
4
|
+
|
|
5
|
+
Centralized analytics and tracking package for all CodeSwayam applications. A single `<Analytics />` component that loads GTM, GA4, Meta Pixel, Hotjar, and Microsoft Clarity — plus Google Search Console verification — all configurable from the Admin Panel or environment variables.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @codeswayam/analytics
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Peer dependencies:** `react ^19`, `next ^16`
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
Add `<Analytics />` to your app's root layout. All props are optional — only the tools you pass IDs for will be loaded.
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// app/layout.tsx
|
|
25
|
+
import { Analytics } from '@codeswayam/analytics';
|
|
26
|
+
|
|
27
|
+
export default function RootLayout({ children }) {
|
|
28
|
+
return (
|
|
29
|
+
<html>
|
|
30
|
+
<body>
|
|
31
|
+
<Analytics
|
|
32
|
+
gtmId={process.env.NEXT_PUBLIC_GTM_ID}
|
|
33
|
+
ga4Id={process.env.NEXT_PUBLIC_GA4_ID}
|
|
34
|
+
metaPixelId={process.env.NEXT_PUBLIC_META_PIXEL_ID}
|
|
35
|
+
gscVerification={process.env.NEXT_PUBLIC_GSC_VERIFICATION}
|
|
36
|
+
hotjarId={process.env.NEXT_PUBLIC_HOTJAR_ID}
|
|
37
|
+
clarityId={process.env.NEXT_PUBLIC_CLARITY_ID}
|
|
38
|
+
appName="web"
|
|
39
|
+
/>
|
|
40
|
+
{children}
|
|
41
|
+
</body>
|
|
42
|
+
</html>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
> If no IDs are provided, the component renders nothing.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Props
|
|
52
|
+
|
|
53
|
+
| Prop | Type | Description |
|
|
54
|
+
|------|------|-------------|
|
|
55
|
+
| `gtmId` | `string` | Google Tag Manager container ID (e.g. `GTM-XXXXXX`) |
|
|
56
|
+
| `ga4Id` | `string` | GA4 Measurement ID (e.g. `G-XXXXXXXXXX`). Skipped if `gtmId` is set — GTM handles GA4 |
|
|
57
|
+
| `metaPixelId` | `string` | Meta (Facebook) Pixel ID |
|
|
58
|
+
| `gscVerification` | `string` | Google Search Console meta verification code |
|
|
59
|
+
| `hotjarId` | `string` | Hotjar Site ID |
|
|
60
|
+
| `clarityId` | `string` | Microsoft Clarity Project ID |
|
|
61
|
+
| `appName` | `string` | Pushed to `dataLayer` as `appName` for GTM filtering across apps |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Dynamic Config from Admin Panel
|
|
66
|
+
|
|
67
|
+
Fetch analytics IDs from the core-api at runtime — no redeploy needed when IDs change.
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// app/layout.tsx (server component)
|
|
71
|
+
import { Analytics, getAnalyticsConfig } from '@codeswayam/analytics';
|
|
72
|
+
|
|
73
|
+
export default async function RootLayout({ children }) {
|
|
74
|
+
const config = await getAnalyticsConfig('web'); // fetches from /admin/analytics/config/web
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<html>
|
|
78
|
+
<body>
|
|
79
|
+
<Analytics {...config} appName="web" />
|
|
80
|
+
{children}
|
|
81
|
+
</body>
|
|
82
|
+
</html>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`getAnalyticsConfig(appId)` fetches from `{API_URL}/admin/analytics/config/{appId}` with a 5-minute Next.js cache (`revalidate: 300`). Falls back to an empty config on error.
|
|
88
|
+
|
|
89
|
+
### App IDs
|
|
90
|
+
|
|
91
|
+
| App | `appId` |
|
|
92
|
+
|-----|---------|
|
|
93
|
+
| codeswayam-web | `web` |
|
|
94
|
+
| codeswayam-auth | `auth` |
|
|
95
|
+
| Auraflow | `auraflow` |
|
|
96
|
+
| EMS Frontend | `ems` |
|
|
97
|
+
| NeuralHub | `neural` |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Custom Event Tracking
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { trackEvent } from '@codeswayam/analytics';
|
|
105
|
+
|
|
106
|
+
// Fires to GTM dataLayer + Meta Pixel
|
|
107
|
+
trackEvent('button_click', { button_id: 'cta-hero', page: '/home' });
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Page View Tracking
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { trackPageView } from '@codeswayam/analytics';
|
|
114
|
+
|
|
115
|
+
trackPageView('/dashboard');
|
|
116
|
+
// Pushes page_view to dataLayer + fires Meta Pixel PageView
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Both functions are no-ops on the server (SSR-safe).
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Environment Variables
|
|
124
|
+
|
|
125
|
+
| Variable | Description |
|
|
126
|
+
|----------|-------------|
|
|
127
|
+
| `NEXT_PUBLIC_GTM_ID` | Google Tag Manager container ID |
|
|
128
|
+
| `NEXT_PUBLIC_GA4_ID` | GA4 Measurement ID |
|
|
129
|
+
| `NEXT_PUBLIC_META_PIXEL_ID` | Meta Pixel ID |
|
|
130
|
+
| `NEXT_PUBLIC_GSC_VERIFICATION` | Google Search Console verification code |
|
|
131
|
+
| `NEXT_PUBLIC_HOTJAR_ID` | Hotjar Site ID |
|
|
132
|
+
| `NEXT_PUBLIC_CLARITY_ID` | Microsoft Clarity Project ID |
|
|
133
|
+
| `NEXT_PUBLIC_API_URL` / `API_URL` | Core API base URL (for `getAnalyticsConfig`) |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Exports
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
Analytics // React component — loads all tracking scripts
|
|
141
|
+
getAnalyticsConfig // Server utility — fetches config from core-api
|
|
142
|
+
trackEvent // Fire a custom event to GTM dataLayer + Meta Pixel
|
|
143
|
+
trackPageView // Fire a page view event
|
|
144
|
+
AnalyticsConfig // TypeScript interface for the config shape
|
|
145
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codeswayam/analytics",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Centralized analytics and tracking for CodeSwayam apps",
|
|
5
|
+
"main": "./src/index.tsx",
|
|
6
|
+
"types": "./src/index.tsx",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"react": "^19.2.3",
|
|
13
|
+
"next": "^16.2.4"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/react": "^19",
|
|
17
|
+
"typescript": "^5"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": "^19.2.3",
|
|
21
|
+
"next": "^16.2.4"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Script from 'next/script';
|
|
4
|
+
|
|
5
|
+
export interface AnalyticsConfig {
|
|
6
|
+
gtmId?: string;
|
|
7
|
+
ga4Id?: string;
|
|
8
|
+
metaPixelId?: string;
|
|
9
|
+
gscVerification?: string;
|
|
10
|
+
hotjarId?: string;
|
|
11
|
+
clarityId?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface AnalyticsProps extends AnalyticsConfig {
|
|
15
|
+
appName?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function Analytics({
|
|
19
|
+
gtmId,
|
|
20
|
+
ga4Id,
|
|
21
|
+
metaPixelId,
|
|
22
|
+
gscVerification,
|
|
23
|
+
hotjarId,
|
|
24
|
+
clarityId,
|
|
25
|
+
appName,
|
|
26
|
+
}: AnalyticsProps) {
|
|
27
|
+
if (!gtmId && !ga4Id && !metaPixelId && !hotjarId && !clarityId) return null;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
{gscVerification && (
|
|
32
|
+
<meta name="google-site-verification" content={gscVerification} />
|
|
33
|
+
)}
|
|
34
|
+
|
|
35
|
+
{gtmId && (
|
|
36
|
+
<>
|
|
37
|
+
<Script id="gtm-script" strategy="afterInteractive">
|
|
38
|
+
{`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
|
39
|
+
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
40
|
+
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
41
|
+
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
42
|
+
})(window,document,'script','dataLayer','${gtmId}');
|
|
43
|
+
${appName ? `window.dataLayer=window.dataLayer||[];window.dataLayer.push({'appName':'${appName}'});` : ''}`}
|
|
44
|
+
</Script>
|
|
45
|
+
<noscript>
|
|
46
|
+
<iframe src={`https://www.googletagmanager.com/ns.html?id=${gtmId}`}
|
|
47
|
+
height="0" width="0" style={{ display: 'none', visibility: 'hidden' }} />
|
|
48
|
+
</noscript>
|
|
49
|
+
</>
|
|
50
|
+
)}
|
|
51
|
+
|
|
52
|
+
{ga4Id && !gtmId && (
|
|
53
|
+
<>
|
|
54
|
+
<Script src={`https://www.googletagmanager.com/gtag/js?id=${ga4Id}`} strategy="afterInteractive" />
|
|
55
|
+
<Script id="ga4-script" strategy="afterInteractive">
|
|
56
|
+
{`window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}
|
|
57
|
+
gtag('js',new Date());gtag('config','${ga4Id}',{'send_page_view':true});`}
|
|
58
|
+
</Script>
|
|
59
|
+
</>
|
|
60
|
+
)}
|
|
61
|
+
|
|
62
|
+
{metaPixelId && (
|
|
63
|
+
<>
|
|
64
|
+
<Script id="meta-pixel" strategy="afterInteractive">
|
|
65
|
+
{`!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
|
66
|
+
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
|
|
67
|
+
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
|
|
68
|
+
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
|
|
69
|
+
document,'script','https://connect.facebook.net/en_US/fbevents.js');
|
|
70
|
+
fbq('init','${metaPixelId}');fbq('track','PageView');`}
|
|
71
|
+
</Script>
|
|
72
|
+
<noscript>
|
|
73
|
+
<img height="1" width="1" style={{ display: 'none' }}
|
|
74
|
+
src={`https://www.facebook.com/tr?id=${metaPixelId}&ev=PageView&noscript=1`} alt="" />
|
|
75
|
+
</noscript>
|
|
76
|
+
</>
|
|
77
|
+
)}
|
|
78
|
+
|
|
79
|
+
{hotjarId && (
|
|
80
|
+
<Script id="hotjar-script" strategy="afterInteractive">
|
|
81
|
+
{`(function(h,o,t,j,a,r){h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
|
|
82
|
+
h._hjSettings={hjid:${hotjarId},hjsv:6};a=o.getElementsByTagName('head')[0];
|
|
83
|
+
r=o.createElement('script');r.async=1;r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
|
|
84
|
+
a.appendChild(r);})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');`}
|
|
85
|
+
</Script>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
{clarityId && (
|
|
89
|
+
<Script id="clarity-script" strategy="afterInteractive">
|
|
90
|
+
{`(function(c,l,a,r,i,t,y){c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
|
91
|
+
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
|
92
|
+
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
|
93
|
+
})(window,document,"clarity","script","${clarityId}");`}
|
|
94
|
+
</Script>
|
|
95
|
+
)}
|
|
96
|
+
</>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function trackEvent(eventName: string, eventData?: Record<string, any>) {
|
|
101
|
+
if (typeof window === 'undefined') return;
|
|
102
|
+
if (window.dataLayer) window.dataLayer.push({ event: eventName, ...eventData });
|
|
103
|
+
if (window.fbq) window.fbq('trackCustom', eventName, eventData);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function trackPageView(url: string) {
|
|
107
|
+
if (typeof window === 'undefined') return;
|
|
108
|
+
if (window.dataLayer) window.dataLayer.push({ event: 'page_view', page_path: url });
|
|
109
|
+
if (window.fbq) window.fbq('track', 'PageView');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
declare global {
|
|
113
|
+
interface Window {
|
|
114
|
+
dataLayer: any[];
|
|
115
|
+
fbq: any;
|
|
116
|
+
gtag: (...args: any[]) => void;
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/index.tsx
ADDED
package/src/server.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AnalyticsConfig } from './Analytics';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fetch analytics config for a given appId from the core-api.
|
|
5
|
+
* Call this in your Next.js root layout (server component).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const config = await getAnalyticsConfig('web');
|
|
9
|
+
* <Analytics {...config} appName="web" />
|
|
10
|
+
*/
|
|
11
|
+
export async function getAnalyticsConfig(appId: string): Promise<AnalyticsConfig> {
|
|
12
|
+
const apiUrl = process.env.NEXT_PUBLIC_API_URL || process.env.API_URL || 'http://localhost:3000';
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(`${apiUrl}/admin/analytics/config/${appId}`, {
|
|
15
|
+
next: { revalidate: 300 },
|
|
16
|
+
} as RequestInit);
|
|
17
|
+
if (!res.ok) return {};
|
|
18
|
+
return res.json();
|
|
19
|
+
} catch {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "react-jsx"
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules"]
|
|
18
|
+
}
|