Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma strängvärde byggs med konkatenering i stället för att matcha mönstret.
97 lines
2.9 KiB
TypeScript
97 lines
2.9 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect } from 'react'
|
|
import posthog from 'posthog-js'
|
|
import { isAnalyticsEnabled } from '@/lib/analytics/enabled'
|
|
import {
|
|
buildPersonProperties,
|
|
buildGroupProperties,
|
|
type AnalyticsCompanyInput,
|
|
type AnalyticsUserInput,
|
|
} from '@/lib/analytics/properties'
|
|
|
|
/**
|
|
* Attaches the logged-in user and their active company to PostHog.
|
|
*
|
|
* Mounted from app/(dashboard)/layout.tsx, which is the only place that has
|
|
* both the auth user and the resolved company in one render, and gated there
|
|
* on `!isSandbox` so demo companies never pollute funnels or get surveyed.
|
|
*
|
|
* identify() runs on every dashboard load rather than only at login, per
|
|
* PostHog's guidance: with `persistence: 'memory'` (see
|
|
* instrumentation-client.ts) nothing survives a hard reload, so re-identifying
|
|
* on each load is what keeps person-level analytics correct without storing
|
|
* anything on the device.
|
|
*
|
|
* A useEffect is correct here despite the general rule against it: this
|
|
* synchronises with an external, non-React system (the PostHog SDK).
|
|
*/
|
|
export default function AnalyticsIdentify({
|
|
user,
|
|
company,
|
|
identityHash,
|
|
}: {
|
|
user: AnalyticsUserInput
|
|
company: AnalyticsCompanyInput
|
|
/** Server-computed HMAC of userId (lib/analytics/identity-hash.ts). Null
|
|
* when POSTHOG_SECRET_API_KEY is unset, which is normal off hosted. */
|
|
identityHash?: string | null
|
|
}) {
|
|
const { userId, email, fullName, role } = user
|
|
const {
|
|
id: companyId,
|
|
name: companyName,
|
|
entityType,
|
|
accountingFramework,
|
|
paysSalaries,
|
|
trialEndsAt,
|
|
capabilities,
|
|
} = company
|
|
|
|
// The dashboard layout rebuilds the props objects (and the capabilities
|
|
// array) on every render, so depending on them directly would re-fire
|
|
// identify on every navigation. Depend on primitives, and collapse the
|
|
// array to a stable string key.
|
|
const capabilityKey = capabilities ? [...capabilities].sort().join(',') : ''
|
|
|
|
useEffect(() => {
|
|
if (!isAnalyticsEnabled()) return
|
|
|
|
// Verified identity first: it tells PostHog Support this browser really
|
|
// is `userId`, so a support ticket follows the person across devices
|
|
// instead of being scoped to one browser session. Without the secret key
|
|
// this is skipped and tickets fall back to email recovery.
|
|
if (identityHash) posthog.setIdentity(userId, identityHash)
|
|
|
|
posthog.identify(userId, buildPersonProperties({ userId, email, fullName, role }))
|
|
posthog.group(
|
|
'company',
|
|
companyId,
|
|
buildGroupProperties({
|
|
id: companyId,
|
|
name: companyName,
|
|
entityType,
|
|
accountingFramework,
|
|
paysSalaries,
|
|
trialEndsAt,
|
|
capabilities: capabilityKey ? capabilityKey.split(',') : undefined,
|
|
})
|
|
)
|
|
}, [
|
|
userId,
|
|
email,
|
|
fullName,
|
|
role,
|
|
companyId,
|
|
companyName,
|
|
entityType,
|
|
accountingFramework,
|
|
paysSalaries,
|
|
trialEndsAt,
|
|
capabilityKey,
|
|
identityHash,
|
|
])
|
|
|
|
return null
|
|
}
|