Álvaro Valenzuela Valdes
feat: implement IA recommendation engine based on company keywords
8663682
import type { AnalysisHistoryItem, AnalysisResult, CompanyProfile, Tender, PurchaseOrder, TenderDetailInfo } from "./types";
// Auto-detect API base URL based on environment
export function getAPIBase(): string {
// 1. Explicit env var (highest priority)
if (process.env.NEXT_PUBLIC_API_BASE) {
return process.env.NEXT_PUBLIC_API_BASE;
}
if (typeof window === 'undefined') return '';
const hostname = window.location.hostname;
// 2. Local development detection
if (hostname === 'localhost' || hostname === '127.0.0.1') {
return 'http://localhost:8000';
}
// 3. Hugging Face & Production: Use absolute origin for robustness
if (typeof window !== 'undefined') {
const origin = window.location.origin;
console.log('[ANDES-DEBUG] API Origin detected:', origin);
return origin;
}
return '';
}
const API_BASE = getAPIBase();
// Log API base for debugging
if (typeof window !== 'undefined') {
console.log('[API] Final API Base URL:', API_BASE, 'on hostname:', window.location.hostname);
}
const jsonHeaders = {
"Content-Type": "application/json",
};
export async function healthCheck() {
const res = await fetch(`${API_BASE}/api/health`);
if (!res.ok) {
throw new Error("Health check failed");
}
return res.json();
}
export async function fetchDbStatus() {
const res = await fetch(`${API_BASE}/api/admin/db-stats`);
if (!res.ok) return null;
return res.json();
}
export async function searchTenders(params: {
keyword?: string;
buyer?: string;
provider_code?: string;
org_code?: string;
status?: string;
code?: string;
date?: string;
type_code?: string;
skip?: number;
limit?: number;
}): Promise<Tender[]> {
const query = new URLSearchParams();
if (params.keyword) query.append("keyword", params.keyword);
if (params.buyer) query.append("buyer", params.buyer);
if (params.provider_code) query.append("provider_code", params.provider_code);
if (params.org_code) query.append("org_code", params.org_code);
if (params.status) query.append("status", params.status);
if (params.code) query.append("code", params.code);
if (params.date) query.append("date", params.date);
if (params.type_code) query.append("type_code", params.type_code);
if (params.skip !== undefined) query.append("skip", params.skip.toString());
if (params.limit !== undefined) query.append("limit", params.limit.toString());
const res = await fetch(`${API_BASE}/api/tenders?${query.toString()}`);
if (!res.ok) {
throw new Error("Error searching tenders");
}
return res.json();
}
export async function analyzeTender(
tender: Tender,
companyProfile: CompanyProfile,
documentText?: string,
models?: Record<string, string>,
tenderDetails?: TenderDetailInfo | null
): Promise<AnalysisResult> {
const res = await fetch(`${API_BASE}/api/analyze`, {
method: "POST",
headers: jsonHeaders,
body: JSON.stringify({
tender,
company_profile: companyProfile,
document_text: documentText,
models: models,
tender_details: tenderDetails
}),
});
if (!res.ok) {
throw new Error("Error analyzing tender");
}
return res.json();
}
export async function uploadDocument(file: File): Promise<{ text: string; filename: string }> {
const formData = new FormData();
formData.append("file", file);
const res = await fetch(`${API_BASE}/api/upload-document`, {
method: "POST",
body: formData,
});
if (!res.ok) {
throw new Error("Error uploading document");
}
return res.json();
}
export async function saveCompanyProfile(profile: CompanyProfile): Promise<CompanyProfile> {
const res = await fetch(`${API_BASE}/api/company-profile`, {
method: "POST",
headers: jsonHeaders,
body: JSON.stringify(profile),
});
if (!res.ok) {
throw new Error("Error saving company profile");
}
return res.json();
}
export async function fetchCompanyProfile(): Promise<CompanyProfile> {
const res = await fetch(`${API_BASE}/api/company-profile`);
if (!res.ok) {
throw new Error("No company profile available");
}
return res.json();
}
export async function fetchAnalysisHistory(): Promise<AnalysisHistoryItem[]> {
const res = await fetch(`${API_BASE}/api/analysis-history`);
if (!res.ok) {
throw new Error("Error fetching analysis history");
}
return res.json();
}
export async function saveSearchHistory(query: string, resultsCount: number, isAgile: boolean = false) {
return fetch(`${API_BASE}/api/search-history`, {
method: "POST",
headers: jsonHeaders,
body: JSON.stringify({
query,
results_count: resultsCount,
searched_at: new Date().toISOString(),
is_agile: isAgile
})
});
}
export async function fetchSearchHistory(): Promise<any[]> {
const res = await fetch(`${API_BASE}/api/search-history`);
if (!res.ok) return [];
return res.json();
}
export async function syncDatabase() {
const res = await fetch(`${API_BASE}/api/admin/sync-all`, { method: "POST" });
if (!res.ok) {
throw new Error("Error syncing database");
}
return res.json();
}
export async function clearDatabase() {
const res = await fetch(`${API_BASE}/api/admin/db-clear`, { method: "DELETE" });
if (!res.ok) {
throw new Error("Error clearing database");
}
return res.json();
}
export async function fetchDetailedDbStats() {
const res = await fetch(`${API_BASE}/api/admin/db-stats`);
if (!res.ok) return null;
return res.json();
}
export async function fetchRecommendations() {
const res = await fetch(`${API_BASE}/api/tenders/recommendations`);
if (!res.ok) return [];
return res.json();
}
export async function scrapeTenders(keyword: string): Promise<Tender[]> {
const res = await fetch(`${API_BASE}/api/tenders/scrape?keyword=${encodeURIComponent(keyword)}`);
if (!res.ok) {
const errorText = await res.text();
throw new Error(`Scraper error (${res.status}): ${errorText || "Failed to scrape tenders"}`);
}
return res.json();
}
export async function fetchPurchaseOrders(date?: string, status: string = "todos"): Promise<PurchaseOrder[]> {
const query = new URLSearchParams();
if (date) query.append("date", date);
query.append("status", status);
const url = `${API_BASE}/api/purchase-orders?${query.toString()}`;
console.log("[API] Fetching purchase orders from:", url);
const res = await fetch(url);
if (!res.ok) {
const errorText = await res.text();
console.error("[API] Purchase orders error:", res.status, errorText);
throw new Error(`Failed to fetch purchase orders (${res.status}): Check if backend is running at ${API_BASE}`);
}
return res.json();
}
export async function fetchTenderDetails(code: string, qs?: string): Promise<TenderDetailInfo> {
const query = new URLSearchParams();
if (qs) query.append("qs", qs);
const res = await fetch(`${API_BASE}/api/tenders/${code}/detail-tabs?${query.toString()}`);
if (!res.ok) {
throw new Error("Error fetching tender details");
}
return res.json();
}
export async function extractTenderDetails(code: string, qs?: string): Promise<any> {
const query = new URLSearchParams();
if (qs) query.append("qs", qs);
const res = await fetch(`${API_BASE}/api/tenders/${code}/extract-details?${query.toString()}`, {
method: "POST"
});
if (!res.ok) {
throw new Error("Error extracting tender details");
}
return res.json();
}