// Fetches the daily market aggregate series for a given listing-type + // bedroom band. Re-fetches when the inputs change. Returns the raw array // of points plus a derived "now vs N days ago" delta the strip renders. import { useEffect, useState } from 'react'; import type { AuthUser } from '@/auth/types'; import type { MarketTrendPoint } from '@/types'; import { apiRequest } from '@/services/apiClient'; export interface MarketTrendDelta { metric: 'median_total_price' | 'median_qmprice' | 'listing_count'; current: number | null; previous: number | null; changePct: number | null; } export interface UseMarketTrendResult { series: MarketTrendPoint[]; isLoading: boolean; error: string | null; // Convenience: today's value vs the oldest in-window value. deltas: Record; } function buildDelta( metric: MarketTrendDelta['metric'], series: MarketTrendPoint[], ): MarketTrendDelta { if (series.length < 2) { const only = series[0]; return { metric, current: only ? (only[metric] as number | null) : null, previous: null, changePct: null, }; } const current = series[series.length - 1][metric] as number | null; const previous = series[0][metric] as number | null; if (current === null || previous === null || previous === 0) { return { metric, current, previous, changePct: null }; } const changePct = Math.round(((current - previous) / previous) * 1000) / 10; return { metric, current, previous, changePct }; } export function useMarketTrend( user: AuthUser | null, listingType: 'RENT' | 'BUY', minBedrooms: number, maxBedrooms: number, days: number = 30, ): UseMarketTrendResult { const [series, setSeries] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (!user) return; let cancelled = false; setIsLoading(true); setError(null); const params = new URLSearchParams({ listing_type: listingType, min_bedrooms: String(minBedrooms), max_bedrooms: String(maxBedrooms), days: String(days), }); apiRequest(user, `/api/market_trend?${params}`) .then((data) => { if (cancelled) return; setSeries(data); }) .catch((err: Error) => { if (cancelled) return; setError(err.message); }) .finally(() => { if (!cancelled) setIsLoading(false); }); return () => { cancelled = true; }; }, [user, listingType, minBedrooms, maxBedrooms, days]); const deltas: UseMarketTrendResult['deltas'] = { median_total_price: buildDelta('median_total_price', series), median_qmprice: buildDelta('median_qmprice', series), listing_count: buildDelta('listing_count', series), }; return { series, isLoading, error, deltas }; }