import { useQuery } from '@tanstack/react-query'; import { analyticsApi } from '@/lib/analytics-api'; import { useAuthStore } from '@/lib/auth-store'; export const analyticsKeys = { all: ['analytics'] as const, marketReport: (city: string, period: string) => ['analytics', 'market-report', city, period] as const, heatmap: (city: string, period: string) => ['analytics', 'heatmap', city, period] as const, districtStats: (city: string, period: string) => ['analytics', 'district-stats', city, period] as const, priceTrend: (district: string, city: string, propertyType: string, periods: string[]) => ['analytics', 'price-trend', district, city, propertyType, periods] as const, marketSnapshot: (city: string) => ['analytics', 'market-snapshot', city] as const, priceMovers: (direction: 'up' | 'down', period: string) => ['analytics', 'price-movers', direction, period] as const, trendingAreas: (period: number) => ['analytics', 'trending-areas', period] as const, }; /** * Analytics endpoints currently require authentication on the backend. We * gate React Query hooks on `isInitialized` (not `isAuthenticated`) so that: * * - Authenticated visitors fire queries the moment `initialize()` finishes, * even if the React subscription to `isAuthenticated` lags a tick behind * (we previously saw the homepage stay stuck on "Đang tải..." because the * gate stayed `false` after the first render and React-Query never refetched). * - Anonymous visitors fire one request that returns 401 — react-query * handles this gracefully (silent toast-less rejection in api-client) and * the components fall back to empty states. * * The 401 cost for anon users is preferable to a perpetually empty homepage * for authed users. */ function useAuthedAnalytics() { const isInitialized = useAuthStore((s) => s.isInitialized); return isInitialized; } export function useMarketReport(city: string, period: string) { const enabled = useAuthedAnalytics(); return useQuery({ queryKey: analyticsKeys.marketReport(city, period), queryFn: () => analyticsApi.getMarketReport(city, period), enabled, }); } export function useHeatmap(city: string, period: string) { const enabled = useAuthedAnalytics(); return useQuery({ queryKey: analyticsKeys.heatmap(city, period), queryFn: () => analyticsApi.getHeatmap(city, period), enabled, }); } export function useDistrictStats(city: string, period: string) { const enabled = useAuthedAnalytics(); return useQuery({ queryKey: analyticsKeys.districtStats(city, period), queryFn: () => analyticsApi.getDistrictStats(city, period), enabled, }); } export function usePriceTrend( district: string, city: string, propertyType: string, periods: string[], ) { const authed = useAuthedAnalytics(); return useQuery({ queryKey: analyticsKeys.priceTrend(district, city, propertyType, periods), queryFn: () => analyticsApi.getPriceTrend(district, city, propertyType, periods), enabled: authed && !!district && !!city, }); } export function useMarketSnapshot(city: string) { const enabled = useAuthedAnalytics(); return useQuery({ queryKey: analyticsKeys.marketSnapshot(city), queryFn: () => analyticsApi.getMarketSnapshot(city), refetchInterval: 5 * 60 * 1000, enabled, }); } export function usePriceMovers(direction: 'up' | 'down', period = '7d', limit = 5) { const enabled = useAuthedAnalytics(); return useQuery({ queryKey: analyticsKeys.priceMovers(direction, period), queryFn: () => analyticsApi.getPriceMovers(direction, period, limit), enabled, }); } export function useTrendingAreas(period = 7, limit = 10) { const enabled = useAuthedAnalytics(); return useQuery({ queryKey: analyticsKeys.trendingAreas(period), queryFn: () => analyticsApi.getTrendingAreas(period, limit), enabled, }); }