SPB Git

spb/vquant Public MIT

VibeQuant — AI-powered institutional-grade financial intelligence platform.

TypeScript 84.3% Python 11.7% JavaScript 1.6% CSS 1.5% HTML 0.7%
3.1 KB · 111 lines typescript
Raw Blame History
1/*2 * =============================================================================3 *  VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 *  File:      client/src/hooks/useAnalytics.ts6 *7 *  Author:    Simon-Pierre Boucher8 *  Contact:   contact@spboucher.ai9 *  Website:   https://www.spboucher.ai10 *  Demo:      https://www.vquant.ai11 *  License:   MIT (see LICENSE)12 *13 *  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14 * =============================================================================15 */1617import { useEffect, useRef, useState } from 'react';1819/**20 * Hook to send heartbeat to analytics server21 * Tracks user activity and current generation status22 */23export function useAnalytics(status: 'idle' | 'generating' | 'error', currentQuery?: string) {24  const sessionIdRef = useRef<string>(getOrCreateSessionId());25  const lastStatusRef = useRef<string>(status);2627  useEffect(() => {28    const sendHeartbeat = async () => {29      try {30        await fetch('/api/analytics/heartbeat', {31          method: 'POST',32          headers: {33            'Content-Type': 'application/json',34          },35          body: JSON.stringify({36            sessionId: sessionIdRef.current,37            status,38            currentQuery: currentQuery || null,39            userId: null, // TODO: Get from auth context if logged in40          }),41        });42      } catch (error) {43        console.error('Failed to send heartbeat:', error);44      }45    };4647    // Send heartbeat immediately when status changes48    if (status !== lastStatusRef.current) {49      sendHeartbeat();50      lastStatusRef.current = status;51    }5253    // Send heartbeat every 30 seconds while generating54    if (status === 'generating') {55      const interval = setInterval(sendHeartbeat, 30000);56      return () => clearInterval(interval);57    }58  }, [status, currentQuery]);5960  return {61    sessionId: sessionIdRef.current,62  };63}6465/**66 * Get or create a unique session ID for this browser session67 */68function getOrCreateSessionId(): string {69  const key = 'vibequant_session_id';70  let sessionId = sessionStorage.getItem(key);7172  if (!sessionId) {73    sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(7)}`;74    sessionStorage.setItem(key, sessionId);75  }7677  return sessionId;78}7980/**81 * Hook to fetch real-time analytics stats (public endpoint)82 */83export function useRealTimeStats(refreshInterval: number = 10000) {84  const [stats, setStats] = useState<any>(null);85  const [loading, setLoading] = useState(true);8687  useEffect(() => {88    const fetchStats = async () => {89      try {90        const response = await fetch('/api/analytics/real-time-stats');91        if (response.ok) {92          const data = await response.json();93          setStats(data);94        }95      } catch (error) {96        console.error('Failed to fetch real-time stats:', error);97      } finally {98        setLoading(false);99      }100    };101102    fetchStats();103    const interval = setInterval(fetchStats, refreshInterval);104105    return () => clearInterval(interval);106  }, [refreshInterval]);107108  return { stats, loading };109}110111