ALwrity Version 0.5.0 (Fastapi + React )
This commit is contained in:
92
frontend/src/api/client.ts
Normal file
92
frontend/src/api/client.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// Create a shared axios instance for all API calls
|
||||
export const apiClient = axios.create({
|
||||
baseURL: 'http://localhost:8000',
|
||||
timeout: 60000, // Increased to 60 seconds for regular API calls
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Create a specialized client for AI operations with extended timeout
|
||||
export const aiApiClient = axios.create({
|
||||
baseURL: 'http://localhost:8000',
|
||||
timeout: 180000, // 3 minutes timeout for AI operations (matching 20-25 second responses)
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Create a specialized client for long-running operations like SEO analysis
|
||||
export const longRunningApiClient = axios.create({
|
||||
baseURL: 'http://localhost:8000',
|
||||
timeout: 300000, // 5 minutes timeout for SEO analysis
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Add request interceptor for logging (optional)
|
||||
apiClient.interceptors.request.use(
|
||||
(config) => {
|
||||
console.log(`Making ${config.method?.toUpperCase()} request to ${config.url}`);
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Add response interceptor for error handling (optional)
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
console.error('API Error:', error.response?.status, error.response?.data);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Add interceptors for AI client
|
||||
aiApiClient.interceptors.request.use(
|
||||
(config) => {
|
||||
console.log(`Making AI ${config.method?.toUpperCase()} request to ${config.url}`);
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
aiApiClient.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
console.error('AI API Error:', error.response?.status, error.response?.data);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Add interceptors for long-running client
|
||||
longRunningApiClient.interceptors.request.use(
|
||||
(config) => {
|
||||
console.log(`Making long-running ${config.method?.toUpperCase()} request to ${config.url}`);
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
longRunningApiClient.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
console.error('Long-running API Error:', error.response?.status, error.response?.data);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
177
frontend/src/api/componentLogic.ts
Normal file
177
frontend/src/api/componentLogic.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
// Component Logic API integration
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { apiClient } from './client';
|
||||
|
||||
// AI Research Interfaces
|
||||
export interface UserInfoRequest {
|
||||
full_name: string;
|
||||
email: string;
|
||||
company: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface UserInfoResponse {
|
||||
valid: boolean;
|
||||
user_info?: any;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface ResearchPreferencesRequest {
|
||||
research_depth: string;
|
||||
content_types: string[];
|
||||
auto_research: boolean;
|
||||
factual_content: boolean;
|
||||
}
|
||||
|
||||
export interface ResearchPreferencesResponse {
|
||||
valid: boolean;
|
||||
preferences?: any;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface ResearchRequest {
|
||||
topic: string;
|
||||
preferences: ResearchPreferencesRequest;
|
||||
}
|
||||
|
||||
export interface ResearchResponse {
|
||||
success: boolean;
|
||||
topic: string;
|
||||
results?: any;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
// Personalization Interfaces
|
||||
export interface ContentStyleRequest {
|
||||
writing_style: string;
|
||||
tone: string;
|
||||
content_length: string;
|
||||
}
|
||||
|
||||
export interface ContentStyleResponse {
|
||||
valid: boolean;
|
||||
style_config?: any;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface BrandVoiceRequest {
|
||||
personality_traits: string[];
|
||||
voice_description?: string;
|
||||
keywords?: string;
|
||||
}
|
||||
|
||||
export interface BrandVoiceResponse {
|
||||
valid: boolean;
|
||||
brand_config?: any;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface AdvancedSettingsRequest {
|
||||
seo_optimization: boolean;
|
||||
readability_level: string;
|
||||
content_structure: string[];
|
||||
}
|
||||
|
||||
export interface PersonalizationSettingsRequest {
|
||||
content_style: ContentStyleRequest;
|
||||
brand_voice: BrandVoiceRequest;
|
||||
advanced_settings: AdvancedSettingsRequest;
|
||||
}
|
||||
|
||||
export interface PersonalizationSettingsResponse {
|
||||
valid: boolean;
|
||||
settings?: any;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
// Research Utilities Interfaces
|
||||
export interface ResearchTopicRequest {
|
||||
topic: string;
|
||||
api_keys: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ResearchResultResponse {
|
||||
success: boolean;
|
||||
topic: string;
|
||||
data?: any;
|
||||
error?: string;
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
// AI Research API Functions
|
||||
export async function validateUserInfo(request: UserInfoRequest): Promise<UserInfoResponse> {
|
||||
const res: AxiosResponse<UserInfoResponse> = await apiClient.post('/api/onboarding/ai-research/validate-user', request);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function configureResearchPreferences(request: ResearchPreferencesRequest): Promise<ResearchPreferencesResponse> {
|
||||
const res: AxiosResponse<ResearchPreferencesResponse> = await apiClient.post('/api/onboarding/ai-research/configure-preferences', request);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function processResearchRequest(request: ResearchRequest): Promise<ResearchResponse> {
|
||||
const res: AxiosResponse<ResearchResponse> = await apiClient.post('/api/onboarding/ai-research/process-research', request);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getResearchConfigurationOptions(): Promise<any> {
|
||||
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/ai-research/configuration-options');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getResearchPreferences(): Promise<ResearchPreferencesResponse> {
|
||||
const res: AxiosResponse<ResearchPreferencesResponse> = await apiClient.get('/api/onboarding/ai-research/preferences');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
// Personalization API Functions
|
||||
export async function validateContentStyle(request: ContentStyleRequest): Promise<ContentStyleResponse> {
|
||||
const res: AxiosResponse<ContentStyleResponse> = await apiClient.post('/api/onboarding/personalization/validate-style', request);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function configureBrandVoice(request: BrandVoiceRequest): Promise<BrandVoiceResponse> {
|
||||
const res: AxiosResponse<BrandVoiceResponse> = await apiClient.post('/api/onboarding/personalization/configure-brand', request);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function processPersonalizationSettings(request: PersonalizationSettingsRequest): Promise<PersonalizationSettingsResponse> {
|
||||
const res: AxiosResponse<PersonalizationSettingsResponse> = await apiClient.post('/api/onboarding/personalization/process-settings', request);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getPersonalizationConfigurationOptions(): Promise<any> {
|
||||
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/personalization/configuration-options');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function generateContentGuidelines(settings: any): Promise<any> {
|
||||
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/personalization/generate-guidelines', settings);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
// Research Utilities API Functions
|
||||
export async function processResearchTopic(request: ResearchTopicRequest): Promise<ResearchResultResponse> {
|
||||
const res: AxiosResponse<ResearchResultResponse> = await apiClient.post('/api/onboarding/research/process-topic', request);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function processResearchResults(results: any): Promise<any> {
|
||||
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/research/process-results', results);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function validateResearchRequest(topic: string, api_keys: Record<string, string>): Promise<any> {
|
||||
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/research/validate-request', { topic, api_keys });
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getResearchProvidersInfo(): Promise<any> {
|
||||
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/research/providers-info');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function generateResearchReport(results: any): Promise<any> {
|
||||
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/research/generate-report', results);
|
||||
return res.data;
|
||||
}
|
||||
163
frontend/src/api/onboarding.ts
Normal file
163
frontend/src/api/onboarding.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
// Make sure to install axios: npm install axios
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { apiClient } from './client';
|
||||
|
||||
export interface APIKeyRequest {
|
||||
provider: string;
|
||||
api_key: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface APIKeyResponse {
|
||||
provider: string;
|
||||
api_key: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface OnboardingStepResponse {
|
||||
step: number;
|
||||
data?: any;
|
||||
validation_errors?: string[];
|
||||
}
|
||||
|
||||
export interface OnboardingSessionResponse {
|
||||
id: number;
|
||||
user_id: number;
|
||||
current_step: number;
|
||||
progress: number;
|
||||
}
|
||||
|
||||
export interface OnboardingProgressResponse {
|
||||
progress: number;
|
||||
current_step: number;
|
||||
total_steps: number;
|
||||
completion_percentage: number;
|
||||
}
|
||||
|
||||
export async function startOnboarding() {
|
||||
const res: AxiosResponse<OnboardingSessionResponse> = await apiClient.post('/api/onboarding/start');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getCurrentStep() {
|
||||
// Get the current step from the onboarding status
|
||||
console.log('getCurrentStep: Calling /api/onboarding/status');
|
||||
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/status');
|
||||
console.log('getCurrentStep: Backend returned:', res.data);
|
||||
return { step: res.data.current_step || 1 };
|
||||
}
|
||||
|
||||
export async function setCurrentStep(step: number) {
|
||||
// Complete the current step to move to the next one
|
||||
console.log('setCurrentStep: Completing step', step);
|
||||
const res: AxiosResponse<OnboardingStepResponse> = await apiClient.post(`/api/onboarding/step/${step}/complete`, {
|
||||
data: {},
|
||||
validation_errors: []
|
||||
});
|
||||
console.log('setCurrentStep: Backend response:', res.data);
|
||||
return { step };
|
||||
}
|
||||
|
||||
export async function getApiKeys() {
|
||||
const maxRetries = 3;
|
||||
let lastError: any;
|
||||
|
||||
console.log('getApiKeys: Starting API call to /api/onboarding/api-keys');
|
||||
|
||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||
try {
|
||||
console.log(`getApiKeys: Attempt ${attempt + 1}/${maxRetries}`);
|
||||
const res: AxiosResponse<Record<string, string>> = await apiClient.get('/api/onboarding/api-keys');
|
||||
console.log('getApiKeys: API call successful');
|
||||
return res.data;
|
||||
} catch (error: any) {
|
||||
lastError = error;
|
||||
console.log(`getApiKeys: Attempt ${attempt + 1} failed:`, error.response?.status, error.message);
|
||||
|
||||
// If it's a rate limit error (429), wait and retry
|
||||
if (error.response?.status === 429) {
|
||||
const retryAfter = error.response?.data?.retry_after || 60;
|
||||
const delay = Math.min(retryAfter * 1000, 5000); // Max 5 seconds
|
||||
|
||||
console.log(`getApiKeys: Rate limited, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
continue;
|
||||
}
|
||||
|
||||
// For other errors, don't retry
|
||||
console.log('getApiKeys: Non-rate-limit error, not retrying');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've exhausted all retries, throw the last error
|
||||
console.log('getApiKeys: All retries exhausted');
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
export async function saveApiKey(provider: string, api_key: string, description?: string) {
|
||||
const res: AxiosResponse<APIKeyResponse> = await apiClient.post('/api/onboarding/api-keys', {
|
||||
provider,
|
||||
api_key,
|
||||
description
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getProgress() {
|
||||
const res: AxiosResponse<OnboardingProgressResponse> = await apiClient.get('/api/onboarding/progress');
|
||||
return { progress: res.data.completion_percentage || 0 };
|
||||
}
|
||||
|
||||
export async function setProgress(progress: number) {
|
||||
// Progress is managed automatically by the backend
|
||||
// This function is kept for compatibility but doesn't make a backend call
|
||||
return { progress };
|
||||
}
|
||||
|
||||
// Additional functions for better integration
|
||||
export async function getOnboardingConfig() {
|
||||
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/config');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getStepData(stepNumber: number) {
|
||||
const res: AxiosResponse<any> = await apiClient.get(`/api/onboarding/step/${stepNumber}`);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function skipStep(stepNumber: number) {
|
||||
const res: AxiosResponse<any> = await apiClient.post(`/api/onboarding/step/${stepNumber}/skip`);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function validateApiKeys() {
|
||||
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/api-keys/validate');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function completeOnboarding() {
|
||||
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/complete');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function resetOnboarding() {
|
||||
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/reset');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
// New functions for FinalStep data loading
|
||||
export async function getOnboardingSummary() {
|
||||
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/summary');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getWebsiteAnalysisData() {
|
||||
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/website-analysis');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getResearchPreferencesData() {
|
||||
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/research-preferences');
|
||||
return res.data;
|
||||
}
|
||||
85
frontend/src/api/seoAnalysis.ts
Normal file
85
frontend/src/api/seoAnalysis.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { longRunningApiClient } from './client';
|
||||
import { SEOAnalysisData } from '../components/shared/types';
|
||||
|
||||
// SEO Analysis API functions
|
||||
export const seoAnalysisAPI = {
|
||||
async analyzeURL(url: string, targetKeywords?: string[]): Promise<SEOAnalysisData | null> {
|
||||
try {
|
||||
console.log(`Starting SEO analysis for URL: ${url}`);
|
||||
console.log(`Target keywords:`, targetKeywords);
|
||||
|
||||
const requestData = {
|
||||
url,
|
||||
target_keywords: targetKeywords
|
||||
};
|
||||
console.log('Request data:', requestData);
|
||||
|
||||
const response = await longRunningApiClient.post('/api/seo-dashboard/analyze-comprehensive', requestData);
|
||||
console.log('Response received:', response);
|
||||
console.log('Response data:', response.data);
|
||||
|
||||
if (response.data.success) {
|
||||
console.log(`SEO analysis completed for ${url}`);
|
||||
console.log('Analysis result:', response.data);
|
||||
return response.data;
|
||||
} else {
|
||||
console.error('Analysis failed:', response.data.message);
|
||||
throw new Error(response.data.message || 'Analysis failed');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error analyzing URL:', error);
|
||||
console.error('Error details:', {
|
||||
message: error.message,
|
||||
status: error.response?.status,
|
||||
data: error.response?.data
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async getDetailedMetrics(url: string): Promise<any> {
|
||||
try {
|
||||
console.log(`Getting detailed metrics for URL: ${url}`);
|
||||
const response = await longRunningApiClient.get(`/api/seo-dashboard/metrics/${encodeURIComponent(url)}`);
|
||||
console.log(`Detailed metrics retrieved for ${url}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error getting detailed metrics:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async getAnalysisSummary(): Promise<any> {
|
||||
try {
|
||||
console.log('Getting analysis summary');
|
||||
const response = await longRunningApiClient.get('/api/seo-dashboard/summary');
|
||||
console.log('Analysis summary retrieved');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error getting analysis summary:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async batchAnalyzeURLs(urls: string[]): Promise<any[]> {
|
||||
try {
|
||||
console.log(`Starting batch analysis for ${urls.length} URLs`);
|
||||
const response = await longRunningApiClient.post('/api/seo-dashboard/batch-analyze', { urls });
|
||||
console.log(`Batch analysis completed for ${urls.length} URLs`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error in batch analysis:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async healthCheck(): Promise<boolean> {
|
||||
try {
|
||||
const response = await longRunningApiClient.get('/api/seo-dashboard/health');
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
console.error('Health check failed:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
112
frontend/src/api/seoDashboard.ts
Normal file
112
frontend/src/api/seoDashboard.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { apiClient } from './client';
|
||||
|
||||
export interface SEOHealthScore {
|
||||
score: number;
|
||||
change: number;
|
||||
trend: string;
|
||||
label: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface SEOMetric {
|
||||
value: number;
|
||||
change: number;
|
||||
trend: string;
|
||||
description: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface PlatformStatus {
|
||||
status: string;
|
||||
connected: boolean;
|
||||
last_sync?: string;
|
||||
data_points?: number;
|
||||
}
|
||||
|
||||
export interface AIInsight {
|
||||
insight: string;
|
||||
priority: string;
|
||||
category: string;
|
||||
action_required: boolean;
|
||||
tool_path?: string;
|
||||
}
|
||||
|
||||
export interface SEODashboardData {
|
||||
health_score: SEOHealthScore;
|
||||
key_insight: string;
|
||||
priority_alert: string;
|
||||
metrics: Record<string, SEOMetric>;
|
||||
platforms: Record<string, PlatformStatus>;
|
||||
ai_insights: AIInsight[];
|
||||
last_updated: string;
|
||||
website_url?: string; // User's website URL from onboarding
|
||||
}
|
||||
|
||||
// SEO Dashboard API functions
|
||||
export const seoDashboardAPI = {
|
||||
// Get complete dashboard data
|
||||
async getDashboardData(): Promise<SEODashboardData> {
|
||||
try {
|
||||
const response = await apiClient.get('/api/seo-dashboard/data');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching SEO dashboard data:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get health score only
|
||||
async getHealthScore(): Promise<SEOHealthScore> {
|
||||
try {
|
||||
const response = await apiClient.get('/api/seo-dashboard/health-score');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching SEO health score:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get metrics only
|
||||
async getMetrics(): Promise<Record<string, SEOMetric>> {
|
||||
try {
|
||||
const response = await apiClient.get('/api/seo-dashboard/metrics');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching SEO metrics:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get platform status
|
||||
async getPlatformStatus(): Promise<Record<string, PlatformStatus>> {
|
||||
try {
|
||||
const response = await apiClient.get('/api/seo-dashboard/platforms');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching platform status:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Get AI insights
|
||||
async getAIInsights(): Promise<AIInsight[]> {
|
||||
try {
|
||||
const response = await apiClient.get('/api/seo-dashboard/insights');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching AI insights:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Health check
|
||||
async healthCheck(): Promise<any> {
|
||||
try {
|
||||
const response = await apiClient.get('/api/seo-dashboard/health');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error checking SEO dashboard health:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
289
frontend/src/api/styleDetection.ts
Normal file
289
frontend/src/api/styleDetection.ts
Normal file
@@ -0,0 +1,289 @@
|
||||
/** Style Detection API Integration */
|
||||
|
||||
export interface StyleAnalysisRequest {
|
||||
content: {
|
||||
main_content: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
};
|
||||
analysis_type?: 'comprehensive' | 'patterns';
|
||||
}
|
||||
|
||||
export interface StyleAnalysisResponse {
|
||||
success: boolean;
|
||||
analysis?: any;
|
||||
patterns?: any;
|
||||
guidelines?: any;
|
||||
error?: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface WebCrawlRequest {
|
||||
url?: string;
|
||||
text_sample?: string;
|
||||
}
|
||||
|
||||
export interface WebCrawlResponse {
|
||||
success: boolean;
|
||||
content?: any;
|
||||
metrics?: any;
|
||||
error?: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface StyleDetectionRequest {
|
||||
url?: string;
|
||||
text_sample?: string;
|
||||
include_patterns?: boolean;
|
||||
include_guidelines?: boolean;
|
||||
}
|
||||
|
||||
export interface StyleDetectionResponse {
|
||||
success: boolean;
|
||||
crawl_result?: any;
|
||||
style_analysis?: any;
|
||||
style_patterns?: any;
|
||||
style_guidelines?: any;
|
||||
error?: string;
|
||||
warning?: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
|
||||
|
||||
/**
|
||||
* Analyze content style using AI
|
||||
*/
|
||||
export const analyzeContentStyle = async (request: StyleAnalysisRequest): Promise<StyleAnalysisResponse> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/analyze`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error analyzing content style:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Crawl website content for style analysis
|
||||
*/
|
||||
export const crawlWebsiteContent = async (request: WebCrawlRequest): Promise<WebCrawlResponse> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/crawl`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error crawling website content:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Complete style detection workflow
|
||||
*/
|
||||
export const completeStyleDetection = async (request: StyleDetectionRequest): Promise<StyleDetectionResponse> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/complete`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error in complete style detection:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get style detection configuration options
|
||||
*/
|
||||
export const getStyleDetectionConfiguration = async (): Promise<any> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/configuration-options`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error getting style detection configuration:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate style detection request
|
||||
*/
|
||||
export const validateStyleDetectionRequest = (request: StyleDetectionRequest): { valid: boolean; errors: string[] } => {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!request.url && !request.text_sample) {
|
||||
errors.push('Either URL or text sample is required');
|
||||
}
|
||||
|
||||
if (request.url && !request.url.startsWith('http')) {
|
||||
errors.push('URL must start with http:// or https://');
|
||||
}
|
||||
|
||||
if (request.text_sample && request.text_sample.length < 50) {
|
||||
errors.push('Text sample must be at least 50 characters');
|
||||
}
|
||||
|
||||
if (request.text_sample && request.text_sample.length > 10000) {
|
||||
errors.push('Text sample is too long (max 10,000 characters)');
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if analysis exists for a website URL
|
||||
*/
|
||||
export const checkExistingAnalysis = async (websiteUrl: string): Promise<any> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/check-existing/${encodeURIComponent(websiteUrl)}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error checking existing analysis:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get analysis by ID
|
||||
*/
|
||||
export const getAnalysisById = async (analysisId: number): Promise<any> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/analysis/${analysisId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error getting analysis by ID:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all analyses for the current session
|
||||
*/
|
||||
export const getSessionAnalyses = async (): Promise<any> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/session-analyses`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error getting session analyses:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete an analysis
|
||||
*/
|
||||
export const deleteAnalysis = async (analysisId: number): Promise<any> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/analysis/${analysisId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error deleting analysis:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
};
|
||||
68
frontend/src/api/userData.ts
Normal file
68
frontend/src/api/userData.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { apiClient } from './client';
|
||||
|
||||
export interface UserData {
|
||||
website_url?: string;
|
||||
session?: {
|
||||
id: number;
|
||||
current_step: number;
|
||||
progress: number;
|
||||
started_at?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
website_analysis?: {
|
||||
website_url: string;
|
||||
industry: string;
|
||||
target_audience: string;
|
||||
content_goals: string[];
|
||||
brand_voice: string;
|
||||
content_style: string;
|
||||
};
|
||||
api_keys?: Array<{
|
||||
id: number;
|
||||
provider: string;
|
||||
description?: string;
|
||||
}>;
|
||||
research_preferences?: {
|
||||
target_keywords: string[];
|
||||
competitor_urls: string[];
|
||||
content_topics: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export const userDataAPI = {
|
||||
async getUserData(): Promise<UserData | null> {
|
||||
try {
|
||||
console.log('Fetching user data from backend...');
|
||||
const response = await apiClient.get('/api/user-data');
|
||||
console.log('User data received:', response.data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching user data:', error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async getWebsiteURL(): Promise<string | null> {
|
||||
try {
|
||||
console.log('Fetching website URL...');
|
||||
const response = await apiClient.get('/api/user-data/website-url');
|
||||
console.log('Website URL received:', response.data);
|
||||
return response.data.website_url || null;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching website URL:', error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async getOnboardingData(): Promise<any> {
|
||||
try {
|
||||
console.log('Fetching onboarding data...');
|
||||
const response = await apiClient.get('/api/user-data/onboarding');
|
||||
console.log('Onboarding data received:', response.data);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching onboarding data:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user