On main: session-work-2026-05-22

This commit is contained in:
ajaysi
2026-05-23 13:09:41 +05:30
40 changed files with 1870 additions and 859 deletions

View File

@@ -73,22 +73,14 @@ export const getApiUrl = () => {
throw new Error('REACT_APP_API_URL environment variable is required for production. Please set it in your Vercel project settings.');
}
if (isProduction) {
// Always respect REACT_APP_API_URL if explicitly set — behavior is independent of
// whether the browser is on localhost, ngrok, or any other hostname.
if (apiUrl) {
return apiUrl;
}
// In development, use localhost by default
const envUrl = process.env.REACT_APP_API_URL;
const isLocalhost = typeof window !== 'undefined' && window.location.hostname === 'localhost';
const isNgrok = envUrl && envUrl.includes('ngrok');
if (isLocalhost) {
if (isNgrok) {
console.warn('[apiClient] ⚠️ Overriding ngrok API URL in dev; using http://localhost:8000 to avoid CORS.');
}
return 'http://localhost:8000';
}
// Non-localhost dev (rare): use env if provided, otherwise localhost
return envUrl || 'http://localhost:8000';
// Development fallback when no env var is configured
return 'http://localhost:8000';
};
// Create a shared axios instance for all API calls

View File

@@ -6,20 +6,56 @@ export interface ContentOpportunity {
opportunity: string;
potential_impact: 'High' | 'Medium';
current_position: number;
current_ctr: number;
impressions: number;
clicks: number;
estimated_traffic_gain: number;
priority: 'High' | 'Medium';
suggested_format: string;
}
export interface KeywordGap {
keyword: string;
position: number;
impressions: number;
current_ctr: number;
clicks: number;
estimated_traffic_if_page1: number;
gap_from_page1: number;
}
export interface QuickWin {
keyword: string;
position: number;
impressions: number;
current_ctr: number;
clicks: number;
estimated_traffic_gain: number;
reason: string;
}
export interface PageOpportunity {
page: string;
page_title: string;
impressions: number;
clicks: number;
current_ctr: number;
current_position: number;
reason: string;
}
export interface AIRecommendation {
title: string;
keyword: string;
reason: string;
format: string;
estimated_impact: string;
}
export interface AIRecommendations {
immediate_opportunities: string[];
content_strategy: string[];
long_term_strategy: string[];
immediate_opportunities: AIRecommendation[];
content_strategy: AIRecommendation[];
long_term_strategy: AIRecommendation[];
}
export interface BrainstormSummary {
@@ -30,20 +66,24 @@ export interface BrainstormSummary {
total_clicks: number;
avg_ctr: number;
avg_position: number;
ctr_vs_benchmark: number;
health_score: number;
keyword_distribution: {
positions_1_3: number;
positions_4_10: number;
positions_11_20: number;
positions_21_plus: number;
};
top_keywords: Array<{ keyword: string; impressions: number; position: number }>;
top_pages: Array<{ page: string; clicks: number; impressions: number }>;
top_keywords: Array<{ keyword: string; impressions: number; clicks: number; position: number; ctr: number }>;
top_pages: Array<{ page: string; clicks: number; impressions: number; ctr: number }>;
}
export interface BrainstormResult {
error?: string;
content_opportunities: ContentOpportunity[];
keyword_gaps: KeywordGap[];
quick_wins: QuickWin[];
page_opportunities: PageOpportunity[];
ai_recommendations: AIRecommendations | Record<string, never>;
summary: BrainstormSummary | Record<string, never>;
}