Update index.html

This commit is contained in:
quantumtaskai 2025-06-16 20:27:41 +05:30 committed by GitHub
parent ca1fbaba57
commit 95f70b8454
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -6,7 +6,6 @@
<title>Trade Intelligence - AI Trade Management</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
// Configure Tailwind for proper styling
tailwind.config = {
theme: {
extend: {
@ -85,14 +84,14 @@
</div>
<div>
<h1 class="text-lg font-bold text-white">Trade Intelligence</h1>
<p class="text-sm text-white">Victor AI Agent</p>
<p class="text-sm text-white">AI Trade Intelligence</p>
</div>
</div>
<div class="hidden md:flex items-center gap-6">
<div class="text-right">
<p class="text-sm text-white">Last updated</p>
<p class="font-semibold text-white">6:08:40 AM</p>
<p class="font-semibold text-white" id="lastUpdated"></p>
</div>
<div class="flex items-center gap-4">
<div class="flex items-center gap-2">
@ -444,8 +443,8 @@
</div>
<script>
// n8n Authentication System
const N8N_AUTH_WEBHOOK = 'https://m8taq6tk.rpcld.cc/webhook/login';
// Authentication System
const AUTH_WEBHOOK = 'https://m8taq6tk.rpcld.cc/webhook/login';
function checkAuthentication() {
const isAuthenticated = localStorage.getItem('authenticated');
@ -474,9 +473,7 @@ async function handleLogin(event) {
submitButton.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Signing In...';
try {
console.log('Attempting login with n8n webhook...');
const response = await fetch(N8N_AUTH_WEBHOOK, {
const response = await fetch(AUTH_WEBHOOK, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -493,7 +490,6 @@ async function handleLogin(event) {
}
const result = await response.json();
console.log('Authentication response:', result);
if (result.success === true) {
// Successful login
@ -514,7 +510,6 @@ async function handleLogin(event) {
document.getElementById('password').value = '';
}
} catch (error) {
console.error('Login error:', error);
errorDiv.textContent = 'Login failed. Please check your connection and try again.';
errorDiv.classList.remove('hidden');
document.getElementById('password').value = '';
@ -533,12 +528,10 @@ function logout() {
// Check if required dependencies are loaded
if (typeof supabase === 'undefined') {
console.error('Supabase library not loaded');
alert('Failed to load Supabase. Please refresh the page.');
}
if (typeof APP_CONFIG === 'undefined') {
console.error('APP_CONFIG not loaded');
alert('Configuration not loaded. Please check config.js');
}
@ -549,6 +542,99 @@ const tableFields = {
gulfood: ["company_name", "address", "phone_number", "mail_id", "website"]
};
// Validate and normalize AI-generated filters
function validateAndNormalizeFilters(filters) {
if (!filters || !Array.isArray(filters)) {
return [];
}
return filters.map(filter => {
// Ensure filter has required properties
if (!filter || typeof filter !== 'object') {
return null;
}
// Normalize filter structure
const normalizedFilter = {
field: filter.field || 'all',
operator: filter.operator || 'ilike',
value: filter.value || ''
};
// If the value doesn't have % wildcards, add them
if (normalizedFilter.operator === 'ilike' && normalizedFilter.value &&
!normalizedFilter.value.startsWith('%') && !normalizedFilter.value.endsWith('%')) {
normalizedFilter.value = `%${normalizedFilter.value}%`;
}
return normalizedFilter;
}).filter(filter => filter !== null); // Remove invalid filters
}
// Smart filter function for cross-table compatibility
function filterForTable(filters, tableName) {
const validFields = tableFields[tableName];
return filters.filter(filter => {
// Always include fields that exist in this table
if (validFields.includes(filter.field)) {
return true;
}
// Handle cross-table company name searches
if (filter.field === 'buyer' && tableName === 'seller') {
// Map buyer field to seller field
filter.field = 'seller';
return true;
} else if (filter.field === 'seller' && tableName === 'buyer') {
// Map seller field to buyer field
filter.field = 'buyer';
return true;
} else if ((filter.field === 'buyer' || filter.field === 'seller') && tableName === 'gulfood') {
// Map buyer/seller field to company_name for gulfood
filter.field = 'company_name';
return true;
} else if (filter.field === 'company_name' && (tableName === 'buyer' || tableName === 'seller')) {
// Map company_name to appropriate field for buyer/seller tables
filter.field = tableName;
return true;
}
// Handle generic company searches - create appropriate company name filter for each table
const companySearchFields = ['company', 'name', 'business_name', 'firm', 'entity', 'all'];
if (companySearchFields.includes(filter.field)) {
if (tableName === 'buyer') {
filter.field = 'buyer';
return true;
} else if (tableName === 'seller') {
filter.field = 'seller';
return true;
} else if (tableName === 'gulfood') {
filter.field = 'company_name';
return true;
}
}
// CRITICAL: Handle when AI generates 'all' field for specific company searches
// This ensures we search ALL tables when looking for a specific company
if (filter.field === 'all') {
if (tableName === 'buyer') {
filter.field = 'buyer';
return true;
} else if (tableName === 'seller') {
filter.field = 'seller';
return true;
} else if (tableName === 'gulfood') {
filter.field = 'company_name';
return true;
}
}
// Exclude incompatible fields
return false;
}).map(filter => ({...filter})); // Clone to avoid modifying original
}
let editContext = { id: null, table: null };
// Simple cache implementation
@ -740,7 +826,6 @@ async function submitModalEdit() {
const { error } = await client.from(editContext.table).update(update).eq("id", editContext.id);
if (error) {
console.error('Update error:', error);
showToast("Update failed: " + error.message, 'error');
return;
}
@ -754,15 +839,10 @@ async function submitModalEdit() {
performSearch();
}
} catch (error) {
console.error('Submit edit error:', error);
showToast("Update failed: " + error.message, 'error');
}
}
// Event listener moved to DOMContentLoaded
function renderRecord(r) {
const fields = tableFields[r.__table];
const tableColors = {
@ -866,20 +946,17 @@ function renderRecord(r) {
}
async function parseQueryWithAI(naturalQuery) {
// Check if n8n is configured
if (!APP_CONFIG.useN8n || APP_CONFIG.n8nWebhookUrl === 'YOUR_N8N_WEBHOOK_URL_HERE') {
console.log('🔄 n8n not configured, using fallback parsing');
// Check if webhook is configured
if (!APP_CONFIG.useWebhook || APP_CONFIG.webhookUrl === 'YOUR_WEBHOOK_URL_HERE') {
return parseQueryFallback(naturalQuery);
}
try {
console.log('🤖 Parsing query with n8n AI:', naturalQuery);
// Add timeout and better error handling
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15 second timeout for n8n
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15 second timeout
const response = await fetch(APP_CONFIG.n8nWebhookUrl, {
const response = await fetch(APP_CONFIG.webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -904,33 +981,27 @@ async function parseQueryWithAI(naturalQuery) {
const result = await response.json();
// Handle n8n response format
// Handle webhook response format
if (result.success) {
console.log('✅ n8n AI parsing successful:', result);
if (result.fallback) {
console.log(' Used fallback logic in n8n');
}
return {
table: result.table,
filters: result.filters
};
} else {
throw new Error(result.error || 'n8n workflow failed');
throw new Error(result.error || 'webhook failed');
}
} catch (error) {
if (error.name === 'AbortError') {
console.warn('⚠️ n8n AI parsing timeout, using local fallback');
// Timeout occurred
} else if (error.message.includes('Failed to fetch')) {
console.warn('⚠️ Cannot reach n8n webhook, using local fallback. Check your n8n workflow is active.');
// Network error
} else {
console.warn('⚠️ n8n AI parsing failed, using local fallback:', error.message);
// Other error
}
// Fallback to simple keyword-based parsing
const fallbackResult = parseQueryFallback(naturalQuery);
console.log('🔄 Local fallback parsing result:', fallbackResult);
console.log('🔄 Fallback filters:', fallbackResult.filters);
return fallbackResult;
}
}
@ -951,6 +1022,15 @@ function parseQueryFallback(query) {
// Extract potential filters
const filters = [];
// Extract company names from "find" queries
if (lowerQuery.includes("find")) {
const companyNameMatch = query.match(/find\s+(.+)/i);
if (companyNameMatch) {
const companyName = companyNameMatch[1].trim();
filters.push({ field: "all", operator: "ilike", value: `%${companyName}%` });
}
}
// Country filters
const countries = ["india", "uk", "united kingdom", "usa", "united states", "uae", "china", "germany", "france", "italy"];
for (const country of countries) {
@ -984,7 +1064,6 @@ async function fetchFrom(table, filters, page = 0, pageSize = 100) {
// Check cache first
const cachedResult = getCachedResult(cacheKey);
if (cachedResult) {
console.log('Using cached result for:', table);
return cachedResult;
}
@ -1016,7 +1095,6 @@ async function fetchFrom(table, filters, page = 0, pageSize = 100) {
const { data, error, count } = await q;
if (error) {
console.error('Supabase error:', error);
throw new Error(`Database query failed: ${error.message}`);
}
@ -1031,7 +1109,6 @@ async function fetchFrom(table, filters, page = 0, pageSize = 100) {
return result;
} catch (error) {
console.error('Error in fetchFrom:', error);
throw new Error(`Failed to fetch ${table} data: ${error.message}`);
}
}
@ -1081,34 +1158,68 @@ async function performSearch(loadMore = false) {
try {
const ai = await parseQueryWithAI(query);
console.log('🔍 Search query:', query);
console.log('🔍 AI parsing result:', ai);
// CRITICAL: Validate and normalize filters before processing
const validatedFilters = validateAndNormalizeFilters(ai.filters);
// BULLETPROOF: Detect specific company name searches and ensure comprehensive search
const isSpecificCompanySearch = query.toLowerCase().includes('find') &&
validatedFilters.some(f => f.field === 'all' || f.field === 'company_name' || f.field === 'buyer' || f.field === 'seller' ||
(f.value && f.value.length > 10 && /[A-Z]/.test(f.value))); // Long value with capitals likely a company name
if (isSpecificCompanySearch && ai.table === 'all') {
// Find the company search value
const companyFilter = validatedFilters.find(f =>
f.field === 'all' || f.field === 'company_name' || f.field === 'buyer' || f.field === 'seller' ||
(f.value && f.value.length > 10)
);
if (companyFilter) {
const companyValue = companyFilter.value;
// Remove the original filter and add comprehensive filters for all tables
const otherFilters = validatedFilters.filter(f => f !== companyFilter);
// Add filters for all company name fields
validatedFilters.length = 0; // Clear array
validatedFilters.push(
...otherFilters,
{ field: 'buyer', operator: 'ilike', value: companyValue },
{ field: 'seller', operator: 'ilike', value: companyValue },
{ field: 'company_name', operator: 'ilike', value: companyValue }
);
}
}
let newResults = [];
let totalCount = 0;
if (ai.table === "all") {
// Smart filter distribution for cross-table search
const buyerFilters = filterForTable(validatedFilters, "buyer");
const sellerFilters = filterForTable(validatedFilters, "seller");
const gulfoodFilters = filterForTable(validatedFilters, "gulfood");
const [buyerRes, sellerRes, gulfoodRes] = await Promise.all([
fetchFrom("buyer", ai.filters, searchState.page),
fetchFrom("seller", ai.filters, searchState.page),
fetchFrom("gulfood", [], searchState.page) // No filters for gulfood since it has different fields
fetchFrom("buyer", buyerFilters, searchState.page),
fetchFrom("seller", sellerFilters, searchState.page),
fetchFrom("gulfood", gulfoodFilters, searchState.page)
]);
newResults = [...buyerRes.data, ...sellerRes.data, ...gulfoodRes.data];
totalCount = buyerRes.count + sellerRes.count + gulfoodRes.count;
searchState.hasMore = buyerRes.hasMore || sellerRes.hasMore || gulfoodRes.hasMore;
} else if (ai.table === "gulfood") {
// For gulfood table, don't apply country/brand/category filters
const gulfoodFilters = ai.filters.filter(f =>
["company_name", "address", "phone_number", "mail_id", "website"].includes(f.field)
);
console.log('🔍 Gulfood filters:', gulfoodFilters);
// For gulfood table, filter compatible fields only
const gulfoodFilters = filterForTable(validatedFilters, "gulfood");
const result = await fetchFrom(ai.table, gulfoodFilters, searchState.page);
newResults = result.data;
totalCount = result.count;
searchState.hasMore = result.hasMore;
} else {
const result = await fetchFrom(ai.table, ai.filters, searchState.page);
// For buyer/seller tables, filter compatible fields only
const tableFilters = filterForTable(validatedFilters, ai.table);
const result = await fetchFrom(ai.table, tableFilters, searchState.page);
newResults = result.data;
totalCount = result.count;
searchState.hasMore = result.hasMore;
@ -1152,7 +1263,6 @@ async function performSearch(loadMore = false) {
searchState.page++;
} catch (e) {
console.error('Search error:', e);
showErrorMessage(`Search failed: ${e.message}`);
} finally {
// Hide loading state on search button
@ -1254,9 +1364,7 @@ async function loadAllCountries() {
});
allCountries = Array.from(countrySet).sort();
console.log('Loaded countries:', allCountries);
} catch (error) {
console.error('Error loading countries:', error);
// Fallback to common countries
allCountries = [
'India', 'United Kingdom', 'United States', 'UAE', 'China',
@ -1390,7 +1498,6 @@ async function performProductMatching() {
count: count || 0
};
} catch (error) {
console.error('Buyer fetch error:', error);
throw new Error(`Failed to fetch buyers: ${error.message}`);
}
};
@ -1418,7 +1525,6 @@ async function performProductMatching() {
count: count || 0
};
} catch (error) {
console.error('Seller fetch error:', error);
throw new Error(`Failed to fetch sellers: ${error.message}`);
}
};
@ -1463,7 +1569,6 @@ async function performProductMatching() {
}
} catch (error) {
console.error('Product mapping error:', error);
const errorMessage = error.message || 'Unknown error occurred';
document.getElementById("buyersList").innerHTML = `
@ -1638,7 +1743,6 @@ function exportResults() {
showToast(`Exported ${searchState.results.length} records successfully`, 'success');
} catch (error) {
console.error('Export error:', error);
showToast('Export failed: ' + error.message, 'error');
}
}
@ -1648,8 +1752,6 @@ function quickSearch(type) {
if (searchInput) {
searchInput.value = type;
performSearch();
} else {
console.error('Search input not found');
}
}
@ -1681,7 +1783,6 @@ async function deleteRecord(btn) {
try {
const card = btn.closest("[data-id]");
if (!card) {
console.error('Could not find card element');
return;
}
@ -1689,7 +1790,6 @@ async function deleteRecord(btn) {
const id = card.dataset.id;
if (!table || !id) {
console.error('Missing table or id data');
showToast("Could not delete: missing record information", 'error');
return;
}
@ -1698,7 +1798,6 @@ async function deleteRecord(btn) {
const { error } = await client.from(table).delete().eq("id", id);
if (error) {
console.error('Delete error:', error);
showToast("Delete failed: " + error.message, 'error');
return;
}
@ -1711,7 +1810,6 @@ async function deleteRecord(btn) {
performSearch();
}
} catch (error) {
console.error('Delete record error:', error);
showToast("Delete failed: " + error.message, 'error');
}
}
@ -1792,17 +1890,12 @@ async function handleCreateSubmit(event) {
}
}
console.log('Attempting to insert into table:', table);
console.log('Data to insert:', data);
const { data: result, error } = await client.from(table).insert([data]);
if (error) {
console.error('Create error details:', error);
showToast("Create failed: " + (error.message || JSON.stringify(error)), 'error');
return;
}
console.log('Insert successful:', result);
showToast("Record created successfully!", 'success');
clearCache();
closeCreateModal();
@ -1812,20 +1905,30 @@ async function handleCreateSubmit(event) {
performSearch();
}
} catch (error) {
console.error('Create record error:', error);
showToast("Create failed: " + (error.message || JSON.stringify(error)), 'error');
}
}
function updateLastUpdated() {
const now = new Date();
const timeString = now.toLocaleTimeString('en-US', {
hour12: true,
hour: 'numeric',
minute: '2-digit',
second: '2-digit'
});
const lastUpdatedElement = document.getElementById('lastUpdated');
if (lastUpdatedElement) {
lastUpdatedElement.textContent = timeString;
}
}
function initializeDashboard() {
try {
console.log('Initializing dashboard...');
// Check if required elements exist
const searchInput = document.getElementById('searchInput');
if (!searchInput) {
console.error('Search input not found');
return;
}
@ -1839,10 +1942,11 @@ function initializeDashboard() {
// Load countries for suggestions
loadAllCountries();
console.log('Dashboard initialized successfully');
// Update last updated time
updateLastUpdated();
setInterval(updateLastUpdated, 1000);
} catch (error) {
console.error('Initialization error:', error);
alert('Failed to initialize dashboard. Please refresh the page.');
}
}
@ -1891,6 +1995,5 @@ input:focus, select:focus, button:focus {
}
</style>
</body>
</html>