Add POI feature and imporve the overall backend implementation
This commit is contained in:
209
frontend/src/hooks/usePOIContext.ts
Normal file
209
frontend/src/hooks/usePOIContext.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* POI Context React Hook
|
||||
*
|
||||
* Custom hook for managing POI context state and operations
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { poiContextApi } from '@/services/api/poiContextApi';
|
||||
import type {
|
||||
POIContext,
|
||||
POIDetectionResponse,
|
||||
FeatureImportanceResponse,
|
||||
CompetitorAnalysis
|
||||
} from '@/types/poi';
|
||||
|
||||
export interface UsePOIContextOptions {
|
||||
tenantId: string;
|
||||
autoFetch?: boolean;
|
||||
}
|
||||
|
||||
export interface UsePOIContextResult {
|
||||
poiContext: POIContext | null;
|
||||
isLoading: boolean;
|
||||
isRefreshing: boolean;
|
||||
error: string | null;
|
||||
isStale: boolean;
|
||||
needsRefresh: boolean;
|
||||
featureImportance: FeatureImportanceResponse | null;
|
||||
competitorAnalysis: CompetitorAnalysis | null;
|
||||
competitiveInsights: string[];
|
||||
|
||||
// Actions
|
||||
detectPOIs: (latitude: number, longitude: number, forceRefresh?: boolean) => Promise<void>;
|
||||
refreshPOIs: () => Promise<void>;
|
||||
fetchContext: () => Promise<void>;
|
||||
fetchFeatureImportance: () => Promise<void>;
|
||||
fetchCompetitorAnalysis: () => Promise<void>;
|
||||
deletePOIContext: () => Promise<void>;
|
||||
}
|
||||
|
||||
export function usePOIContext({ tenantId, autoFetch = true }: UsePOIContextOptions): UsePOIContextResult {
|
||||
const [poiContext, setPOIContext] = useState<POIContext | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isStale, setIsStale] = useState(false);
|
||||
const [needsRefresh, setNeedsRefresh] = useState(false);
|
||||
const [featureImportance, setFeatureImportance] = useState<FeatureImportanceResponse | null>(null);
|
||||
const [competitorAnalysis, setCompetitorAnalysis] = useState<CompetitorAnalysis | null>(null);
|
||||
const [competitiveInsights, setCompetitiveInsights] = useState<string[]>([]);
|
||||
|
||||
const fetchContext = useCallback(async () => {
|
||||
if (!tenantId) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await poiContextApi.getPOIContext(tenantId);
|
||||
setPOIContext(response.poi_context);
|
||||
setIsStale(response.is_stale);
|
||||
setNeedsRefresh(response.needs_refresh);
|
||||
} catch (err: any) {
|
||||
if (err.response?.status === 404) {
|
||||
// No POI context found - this is normal for new tenants
|
||||
setPOIContext(null);
|
||||
setError(null);
|
||||
} else {
|
||||
setError(err.message || 'Failed to fetch POI context');
|
||||
console.error('Error fetching POI context:', err);
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
const detectPOIs = useCallback(async (
|
||||
latitude: number,
|
||||
longitude: number,
|
||||
forceRefresh: boolean = false
|
||||
) => {
|
||||
if (!tenantId) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await poiContextApi.detectPOIs(
|
||||
tenantId,
|
||||
latitude,
|
||||
longitude,
|
||||
forceRefresh
|
||||
);
|
||||
|
||||
setPOIContext(response.poi_context);
|
||||
setIsStale(false);
|
||||
setNeedsRefresh(false);
|
||||
|
||||
// Update competitor analysis if available
|
||||
if (response.competitor_analysis) {
|
||||
setCompetitorAnalysis(response.competitor_analysis);
|
||||
}
|
||||
|
||||
// Update competitive insights if available
|
||||
if (response.competitive_insights) {
|
||||
setCompetitiveInsights(response.competitive_insights);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to detect POIs');
|
||||
console.error('Error detecting POIs:', err);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
const refreshPOIs = useCallback(async () => {
|
||||
if (!tenantId) return;
|
||||
|
||||
try {
|
||||
setIsRefreshing(true);
|
||||
setError(null);
|
||||
|
||||
const response = await poiContextApi.refreshPOIContext(tenantId);
|
||||
setPOIContext(response.poi_context);
|
||||
setIsStale(false);
|
||||
setNeedsRefresh(false);
|
||||
|
||||
// Update competitor analysis if available
|
||||
if (response.competitor_analysis) {
|
||||
setCompetitorAnalysis(response.competitor_analysis);
|
||||
}
|
||||
|
||||
// Update competitive insights if available
|
||||
if (response.competitive_insights) {
|
||||
setCompetitiveInsights(response.competitive_insights);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to refresh POI context');
|
||||
console.error('Error refreshing POI context:', err);
|
||||
} finally {
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
const fetchFeatureImportance = useCallback(async () => {
|
||||
if (!tenantId) return;
|
||||
|
||||
try {
|
||||
const response = await poiContextApi.getFeatureImportance(tenantId);
|
||||
setFeatureImportance(response);
|
||||
} catch (err: any) {
|
||||
console.error('Error fetching feature importance:', err);
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
const fetchCompetitorAnalysis = useCallback(async () => {
|
||||
if (!tenantId) return;
|
||||
|
||||
try {
|
||||
const response = await poiContextApi.getCompetitorAnalysis(tenantId);
|
||||
setCompetitorAnalysis(response.competitor_analysis);
|
||||
setCompetitiveInsights(response.insights);
|
||||
} catch (err: any) {
|
||||
console.error('Error fetching competitor analysis:', err);
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
const deletePOIContext = useCallback(async () => {
|
||||
if (!tenantId) return;
|
||||
|
||||
try {
|
||||
await poiContextApi.deletePOIContext(tenantId);
|
||||
setPOIContext(null);
|
||||
setFeatureImportance(null);
|
||||
setCompetitorAnalysis(null);
|
||||
setCompetitiveInsights([]);
|
||||
setIsStale(false);
|
||||
setNeedsRefresh(false);
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to delete POI context');
|
||||
console.error('Error deleting POI context:', err);
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
// Auto-fetch on mount if enabled
|
||||
useEffect(() => {
|
||||
if (autoFetch && tenantId) {
|
||||
fetchContext();
|
||||
}
|
||||
}, [autoFetch, tenantId, fetchContext]);
|
||||
|
||||
return {
|
||||
poiContext,
|
||||
isLoading,
|
||||
isRefreshing,
|
||||
error,
|
||||
isStale,
|
||||
needsRefresh,
|
||||
featureImportance,
|
||||
competitorAnalysis,
|
||||
competitiveInsights,
|
||||
detectPOIs,
|
||||
refreshPOIs,
|
||||
fetchContext,
|
||||
fetchFeatureImportance,
|
||||
fetchCompetitorAnalysis,
|
||||
deletePOIContext
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user