o2o-infinith-demo/src/hooks/useEnrichment.ts
Haewon Kam 79950925a1 fix: add Authorization header to all Edge Function calls + fix Vision Analysis
- All fetch calls to Supabase Edge Functions now include
  Authorization: Bearer <anon_key> (was missing → 401 errors)
- Fix Firecrawl screenshot API: remove invalid screenshotOptions,
  use "screenshot@fullPage" format (v2 API compatibility)
- Fix screenshot response handling: v2 returns URL not base64,
  now downloads and converts to base64 for Gemini Vision
- Add about page to Vision Analysis capture targets
- Add retry utility, channel error tracking, pipeline resume,
  enrichment retry, EmptyState improvements (Sprint 2-3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 10:08:03 +09:00

92 lines
2.6 KiB
TypeScript

import { useState, useEffect, useRef, useCallback } from 'react';
import { enrichChannels, fetchReportById } from '../lib/supabase';
import { mergeEnrichment, type EnrichmentData } from '../lib/transformReport';
import type { MarketingReport } from '../types/report';
type EnrichmentStatus = 'idle' | 'loading' | 'success' | 'error';
interface UseEnrichmentResult {
status: EnrichmentStatus;
enrichedReport: MarketingReport | null;
/** Number of retry attempts made */
retryCount: number;
/** Call this to retry enrichment (max 2 retries) */
retry: () => void;
}
interface EnrichmentParams {
reportId: string | null;
clinicName: string;
instagramHandle?: string;
instagramHandles?: string[];
youtubeChannelId?: string;
facebookHandle?: string;
address?: string;
}
const MAX_RETRIES = 2;
/**
* Triggers background channel enrichment after Phase 1 report renders.
* Fires once, waits for the Edge Function to complete (~27s),
* then returns the merged report. Supports up to 2 manual retries.
*/
export function useEnrichment(
baseReport: MarketingReport | null,
params: EnrichmentParams | null,
): UseEnrichmentResult {
const [status, setStatus] = useState<EnrichmentStatus>('idle');
const [enrichedReport, setEnrichedReport] = useState<MarketingReport | null>(null);
const [retryCount, setRetryCount] = useState(0);
const hasTriggered = useRef(false);
const doEnrich = useCallback(async () => {
if (!baseReport || !params?.reportId) return;
setStatus('loading');
try {
const result = await enrichChannels({
reportId: params.reportId,
clinicName: params.clinicName,
instagramHandle: params.instagramHandle,
instagramHandles: params.instagramHandles,
youtubeChannelId: params.youtubeChannelId,
facebookHandle: params.facebookHandle,
address: params.address,
});
if (result.success && result.data) {
const merged = mergeEnrichment(baseReport, result.data as EnrichmentData);
setEnrichedReport(merged);
setStatus('success');
} else {
setStatus('error');
}
} catch {
setStatus('error');
}
}, [baseReport, params]);
// Initial trigger
useEffect(() => {
if (!baseReport || !params?.reportId || hasTriggered.current) return;
hasTriggered.current = true;
doEnrich();
}, [baseReport, params, doEnrich]);
// Manual retry
const retry = useCallback(() => {
if (retryCount >= MAX_RETRIES) return;
setRetryCount(prev => prev + 1);
doEnrich();
}, [retryCount, doEnrich]);
return {
status,
enrichedReport,
retryCount,
retry,
};
}