import { base44 } from '@/api/base44Client'; import type { User } from '@/types/user'; /* The signed-in user, or null when nobody is or the lookup failed. Fetched once per page load: this is best-effort logging, and a tracking call must never become a second round trip on a user action. */ let _cachedUser: User | null = null; let _userFetched = false; async function getCurrentUser() { if (_userFetched) return _cachedUser; try { _cachedUser = await base44.auth.me(); } catch { _cachedUser = null; } _userFetched = true; return _cachedUser; } /** * The optional context an event may carry. * * Every field is optional because every call site supplies a different subset — * the shape is "whatever this particular event knows". The id fields are the * records the event is about; they are written onto the record only when * supplied, so an event with no application does not get an * `application_id: null` that later reads as a missing link. */ export interface ActivityExtra { email?: string; name?: string; account_type?: string; details?: string; position_id?: string; application_id?: string; candidate_id?: string; interview_id?: string; worker_email?: string; metadata?: unknown; } /** Best-effort activity logging — never throws, never blocks the main flow. */ export async function logActivity(eventType: string, extra: ActivityExtra = {}) { try { const me = await getCurrentUser(); const record = { event_type: eventType, user_email: me?.email || extra.email || 'anonymous', user_name: me?.full_name || extra.name || '', account_type: extra.account_type || me?.account_type || 'unknown', details: extra.details || '', /** * The records this event is about. * * An event that says only "a candidate was contacted" cannot be read back * against the position it was about, so the ids travel with it. They are * written only when supplied — an event with no application does not get * an `application_id: null` that later reads as a missing link. * * Kept as flat fields rather than a metadata blob so the store's own * `filter({ position_id })` can find them without a custom query path. */ ...(extra.position_id ? { position_id: extra.position_id } : {}), ...(extra.application_id ? { application_id: extra.application_id } : {}), ...(extra.candidate_id ? { candidate_id: extra.candidate_id } : {}), ...(extra.interview_id ? { interview_id: extra.interview_id } : {}), ...(extra.worker_email ? { worker_email: extra.worker_email } : {}), ...(extra.metadata ? { metadata: extra.metadata } : {}), }; await base44.entities.UserActivity.create(record); base44.analytics.track({ eventName: eventType, properties: { account_type: record.account_type }, }); } catch { // silent — tracking must not break user flows } }