Victor-AI-Hub/trade-intelligence.html
2025-06-03 12:10:49 +05:30

1245 lines
51 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Trade Intelligence - AI Trade Management</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
body {
font-family: 'Plus Jakarta Sans', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.card-hover {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-hover:hover {
transform: translateY(-4px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeIn 0.4s ease-out;
}
.custom-scrollbar::-webkit-scrollbar {
width: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.loading-skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeLine {
from { opacity: 0; }
to { opacity: 1; }
}
.animate-slide-up {
animation: slideUp 0.4s ease-out;
}
.animate-fade {
animation: fadeLine 0.6s ease-in;
}
.toast {
position: fixed;
bottom: 1rem;
right: 1rem;
z-index: 9999;
max-width: 24rem;
width: 100%;
animation: slideUp 0.3s ease-out;
}
@media (max-width: 640px) {
.toast {
left: 1rem;
right: 1rem;
max-width: none;
}
}
/* Mobile-specific styles */
@media (max-width: 768px) {
.mobile-stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
}
/* Collapsible styles */
.collapsible-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.collapsible-content.open {
max-height: 200px;
}
</style>
</head>
<body>
<!-- Navigation Header -->
<nav class="bg-white shadow-sm border-b sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center py-4">
<div class="flex items-center gap-3">
<button onclick="goHome()" class="w-10 h-10 bg-indigo-100 rounded-lg flex items-center justify-center hover:bg-indigo-200 transition-colors">
<i class="fas fa-home text-indigo-600"></i>
</button>
<div class="w-10 h-10 bg-blue-600 rounded-xl flex items-center justify-center shadow-lg">
<i class="fas fa-ship text-white text-lg"></i>
</div>
<div>
<h1 class="text-lg font-bold text-gray-800">Trade Intelligence</h1>
<p class="text-sm text-gray-500">Victor AI Agent</p>
</div>
</div>
<div class="hidden md:flex items-center gap-6">
<div class="text-right">
<p class="text-sm text-gray-500">Last updated</p>
<p class="font-semibold text-gray-800" id="lastUpdated">Just now</p>
</div>
<div class="flex items-center gap-2">
<div class="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
<span class="text-sm text-green-600 font-medium">Online</span>
</div>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 bg-gray-50 min-h-screen">
<!-- Header Section -->
<div class="text-center mb-8">
<h1 class="text-3xl lg:text-4xl font-bold text-gray-800 mb-4">AI Trade Intelligence</h1>
<p class="text-xl text-gray-600 max-w-2xl mx-auto">
Find exporters, importers, and trade data from around the world. Search our comprehensive database of companies with natural language commands.
</p>
</div>
<!-- Search Command Center -->
<div class="bg-white rounded-2xl p-6 mb-8 shadow-lg border border-gray-200">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center">
<i class="fas fa-brain text-white"></i>
</div>
<h2 class="text-xl font-bold text-gray-800">Trade AI Command</h2>
</div>
<div class="flex flex-col sm:flex-row gap-3">
<div class="flex-1">
<input
id="query"
type="text"
placeholder="Ask AI: 'find exporter from india', 'find importer from uk', 'find gulfood data'"
class="w-full px-4 py-3 rounded-xl border border-gray-300 bg-white text-gray-800 placeholder-gray-500 focus:border-blue-400 focus:ring-2 focus:ring-blue-400/50 outline-none transition-all"
/>
</div>
<button
onclick="callWorkflow()"
class="w-full sm:w-auto bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-xl font-semibold transition-all flex items-center justify-center gap-2 min-w-[120px]"
id="searchBtn"
>
<i class="fas fa-search"></i>
<span>Search</span>
</button>
</div>
<!-- Quick Commands -->
<div class="mt-4 flex flex-wrap gap-2">
<span class="text-sm text-gray-500 mr-2">Quick searches:</span>
<button onclick="setQuickSearch('find exporter from india')" class="text-xs bg-blue-100 hover:bg-blue-200 text-blue-700 px-3 py-1 rounded-full transition-colors">
Exporters from India
</button>
<button onclick="setQuickSearch('find importer from uk')" class="text-xs bg-blue-100 hover:bg-blue-200 text-blue-700 px-3 py-1 rounded-full transition-colors">
UK Importers
</button>
<button onclick="setQuickSearch('find gulfood data')" class="text-xs bg-blue-100 hover:bg-blue-200 text-blue-700 px-3 py-1 rounded-full transition-colors">
Gulfood Exhibition
</button>
</div>
</div>
<!-- CRUD Command Section -->
<div class="hidden lg:block bg-white rounded-2xl p-6 mb-8 shadow-lg border border-gray-200 fade-up">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center">
<i class="fas fa-terminal text-white"></i>
</div>
<h2 class="text-xl font-bold text-gray-800">Create / Update / Delete</h2>
</div>
<div class="flex flex-col sm:flex-row gap-3">
<div class="flex-1">
<input
id="crudCommand"
type="text"
placeholder="e.g., Create importer TechCorp from India email info@techcorp.com"
class="w-full px-4 py-3 rounded-xl border border-gray-300 bg-white text-gray-800 placeholder-gray-500 focus:border-blue-400 focus:ring-2 focus:ring-blue-400/50 outline-none transition-all"
/>
</div>
<button
onclick="callCrudOperation()"
class="w-full sm:w-auto bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-xl font-semibold transition-all flex items-center justify-center gap-2 min-w-[120px]"
id="crudBtn"
>
<i class="fas fa-terminal"></i>
<span>Execute</span>
</button>
</div>
</div>
<!-- Mobile Stats Cards (visible only on mobile) -->
<div class="lg:hidden mb-6">
<div class="mobile-stats-grid">
<div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
<div class="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-search text-blue-600"></i>
</div>
<p class="text-xs text-gray-500 mb-1">Total Searches</p>
<p class="text-sm font-bold text-gray-800" id="mobileSearches">0</p>
</div>
<div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
<div class="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-chart-line text-green-600"></i>
</div>
<p class="text-xs text-gray-500 mb-1">Results Found</p>
<p class="text-sm font-bold text-gray-800" id="mobileResults">0</p>
</div>
<div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
<div class="w-8 h-8 bg-purple-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-globe text-purple-600"></i>
</div>
<p class="text-xs text-gray-500 mb-1">Countries</p>
<p class="text-sm font-bold text-gray-800">150+</p>
</div>
<div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
<div class="w-8 h-8 bg-orange-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-bolt text-orange-600"></i>
</div>
<p class="text-xs text-gray-500 mb-1">Response Time</p>
<p class="text-sm font-bold text-gray-800" id="mobileResponseTime">0ms</p>
</div>
</div>
</div>
<!-- Mobile Collapsible CRUD Section -->
<div class="lg:hidden mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200">
<button
id="mobileCrudToggle"
onclick="toggleMobileCrud()"
class="w-full p-4 flex items-center justify-between text-left"
>
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
<i class="fas fa-terminal text-purple-600 text-lg"></i>
</div>
<div>
<h3 class="font-semibold text-gray-800">Create / Update / Delete</h3>
<p class="text-xs text-gray-500">Execute CRUD operations</p>
</div>
</div>
<i class="fas fa-chevron-down text-gray-400 transition-transform" id="crudChevron"></i>
</button>
<div id="mobileCrudContent" class="collapsible-content">
<div class="px-4 pb-4">
<div class="flex flex-col gap-3">
<input
id="mobileCrudCommand"
type="text"
placeholder="e.g., Create importer TechCorp from India email info@techcorp.com"
class="w-full px-4 py-3 rounded-xl border border-gray-300 focus:border-purple-500 focus:ring-2 focus:ring-purple-200 outline-none transition-all text-sm"
/>
<button
onclick="callMobileCrudOperation()"
class="w-full bg-purple-600 hover:bg-purple-700 text-white px-6 py-3 rounded-xl font-semibold transition-all flex items-center justify-center gap-2"
id="mobileCrudBtn"
>
<i class="fas fa-terminal"></i>
<span>Execute</span>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Stats Dashboard -->
<div class="hidden lg:grid grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
<div class="bg-white rounded-xl p-4 shadow-lg border border-gray-200">
<div class="text-center">
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-search text-blue-600"></i>
</div>
<div class="text-2xl font-bold text-gray-800" id="totalSearches">0</div>
<div class="text-sm text-gray-500">Total Searches</div>
</div>
</div>
<div class="bg-white rounded-xl p-4 shadow-lg border border-gray-200">
<div class="text-center">
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-chart-line text-green-600"></i>
</div>
<div class="text-2xl font-bold text-gray-800" id="totalResults">0</div>
<div class="text-sm text-gray-500">Results Found</div>
</div>
</div>
<div class="bg-white rounded-xl p-4 shadow-lg border border-gray-200">
<div class="text-center">
<div class="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-globe text-purple-600"></i>
</div>
<div class="text-2xl font-bold text-gray-800">150+</div>
<div class="text-sm text-gray-500">Countries</div>
</div>
</div>
<div class="bg-white rounded-xl p-4 shadow-lg border border-gray-200">
<div class="text-center">
<div class="w-10 h-10 bg-orange-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-bolt text-orange-600"></i>
</div>
<div class="text-2xl font-bold text-gray-800" id="responseTime">0ms</div>
<div class="text-sm text-gray-500">Response Time</div>
</div>
</div>
</div>
<!-- Main Content Grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Results Section -->
<div class="lg:col-span-2">
<!-- Results will show here -->
<div id="resultsSection" class="hidden">
<div class="bg-white rounded-2xl p-6 shadow-lg border border-gray-200">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-bold text-gray-800">Search Results</h3>
<div class="flex items-center gap-2">
<p id="resultsCount" class="text-sm text-gray-600"></p>
<button onclick="exportResults()" class="text-sm bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-2 rounded-lg transition-colors flex items-center gap-2">
<i class="fas fa-download text-xs"></i>
Export
</button>
</div>
</div>
<div id="results" class="grid grid-cols-1 gap-4 custom-scrollbar max-h-dvh overflow-y-auto"></div>
</div>
</div>
<!-- Empty State -->
<div id="emptyState" class="bg-white rounded-2xl p-8 shadow-lg border border-gray-200">
<div class="text-center">
<div class="w-20 h-20 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-6">
<i class="fas fa-ship text-blue-600 text-2xl"></i>
</div>
<h3 class="text-xl font-semibold text-gray-800 mb-3">Start Your Trade Search</h3>
<p class="text-gray-600 mb-6">Enter your search query above to find exporters, importers, and trade data from our comprehensive database.</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm">
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
<h4 class="font-medium text-gray-800 mb-2">🚢 Find Exporters</h4>
<p class="text-gray-600">Search by country, product, or company name</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
<h4 class="font-medium text-gray-800 mb-2">📦 Find Importers</h4>
<p class="text-gray-600">Discover potential buyers worldwide</p>
</div>
</div>
</div>
</div>
</div>
<!-- AI Actions Panel -->
<div class="space-y-6">
<!-- Quick Actions -->
<div class="bg-white rounded-2xl p-6 shadow-lg border border-gray-200">
<h3 class="text-lg font-bold text-gray-800 mb-4">Quick Actions</h3>
<div class="space-y-3">
<button onclick="quickSearch('all importers')" class="w-full p-3 bg-blue-600 hover:bg-blue-700 rounded-lg text-white font-medium transition-colors flex items-center gap-3">
<i class="fas fa-search"></i>
<span>All Importers</span>
</button>
<button onclick="quickSearch('all exporters')" class="w-full p-3 bg-green-600 hover:bg-green-700 rounded-lg text-white font-medium transition-colors flex items-center gap-3">
<i class="fas fa-ship"></i>
<span>All Exporters</span>
</button>
<button onclick="quickSearch('gulfood data')" class="w-full p-3 bg-orange-600 hover:bg-orange-700 rounded-lg text-white font-medium transition-colors flex items-center gap-3">
<i class="fas fa-globe"></i>
<span>Gulfood Data</span>
</button>
<button onclick="clearResults()" class="w-full p-3 bg-gray-600 hover:bg-gray-700 rounded-lg text-white font-medium transition-colors flex items-center gap-3">
<i class="fas fa-broom"></i>
<span>Clear Results</span>
</button>
</div>
</div>
<!-- Trade Insights -->
<div class="bg-white rounded-2xl p-6 shadow-lg border border-gray-200">
<h3 class="text-lg font-bold text-gray-800 mb-4">Trade Insights</h3>
<div class="space-y-4">
<div class="bg-blue-50 rounded-lg p-3 border border-blue-200">
<h4 class="font-medium text-blue-700 mb-1">Database Size</h4>
<p class="text-sm text-blue-600">50K+ companies across 150+ countries</p>
</div>
<div class="bg-green-50 rounded-lg p-3 border border-green-200">
<h4 class="font-medium text-green-700 mb-1">Data Freshness</h4>
<p class="text-sm text-green-600">Updated weekly with new trade data</p>
</div>
<div class="bg-orange-50 rounded-lg p-3 border border-orange-200">
<h4 class="font-medium text-orange-700 mb-1">AI Powered</h4>
<p class="text-sm text-orange-600">Natural language search capabilities</p>
</div>
</div>
</div>
<!-- Recent Searches -->
<div class="bg-white rounded-2xl p-6 shadow-lg border border-gray-200">
<h3 class="text-lg font-bold text-gray-800 mb-4">Recent Activity</h3>
<div class="space-y-3 custom-scrollbar max-h-48 overflow-y-auto" id="recentActivity">
<div class="flex items-start gap-3 p-2 bg-gray-50 rounded-lg">
<div class="w-6 h-6 bg-blue-100 rounded flex items-center justify-center flex-shrink-0 mt-1">
<i class="fas fa-search text-blue-600 text-xs"></i>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm text-gray-800">Start searching to see activity</p>
<p class="text-xs text-gray-500">Your recent searches will appear here</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- AI Response Modal -->
<div id="aiModal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4">
<div class="bg-white rounded-2xl max-w-2xl w-full max-h-[80vh] overflow-hidden">
<div class="p-6 border-b border-gray-200">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
<i class="fas fa-robot text-blue-600"></i>
</div>
<h3 class="text-lg font-bold text-gray-800">Trade AI Response</h3>
</div>
<button onclick="closeAIModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="p-6 overflow-y-auto custom-scrollbar" id="aiResponse">
<!-- AI response content will be loaded here -->
</div>
</div>
</div>
<script>
// Navigation function
function goHome() {
if (confirm('Return to AI Services Hub?')) {
alert('Navigating to AI Services Hub...\n\nIn production, this would load: index.html');
}
}
// Global variables
let totalSearchCount = 0;
let totalResultCount = 0;
let currentSearchData = [];
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateLastUpdated();
initializeEventListeners();
});
function initializeEventListeners() {
document.getElementById('query').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
callWorkflow();
}
});
document.getElementById('crudCommand').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
callCrudOperation();
}
});
}
function updateLastUpdated() {
document.getElementById('lastUpdated').textContent = new Date().toLocaleTimeString();
}
function setQuickSearch(query) {
document.getElementById('query').value = query;
callWorkflow();
}
function quickSearch(type) {
const queries = {
'all importers': 'find all importers',
'all exporters': 'find all exporters',
'gulfood data': 'find gulfood data'
};
if (queries[type]) {
document.getElementById('query').value = queries[type];
callWorkflow();
}
}
// Enhanced search function
async function callWorkflow() {
const query = document.getElementById('query').value.trim();
if (!query) {
showToast('Please enter a search query', 'warning');
return;
}
const searchBtn = document.getElementById('searchBtn');
const results = document.getElementById('results');
const resultsSection = document.getElementById('resultsSection');
const resultsCount = document.getElementById('resultsCount');
const emptyState = document.getElementById('emptyState');
// Show loading state
searchBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Searching...';
searchBtn.disabled = true;
resultsSection.classList.add('hidden');
emptyState.classList.add('hidden');
// Show loading skeleton
results.innerHTML = Array(6).fill().map(() => `
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
<div class="animate-pulse">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-6 bg-gray-200 rounded"></div>
<div class="h-5 bg-gray-200 rounded flex-1"></div>
</div>
<div class="space-y-2">
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
<div class="h-4 bg-gray-200 rounded w-2/3"></div>
</div>
</div>
</div>
`).join('');
resultsSection.classList.remove('hidden');
const startTime = Date.now();
try {
console.log('Making API request with query:', query);
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
console.log('Response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log('API Response:', data);
const responseTime = Date.now() - startTime;
// Process the API response
let cards = '';
let resultCount = 0;
if (data && Array.isArray(data) && data.length > 0) {
const apiData = data[0];
console.log('Processing API data:', apiData);
if (apiData.success && apiData.content) {
const messageContent = apiData.content.originalMessage || apiData.content.message;
console.log('Message content:', messageContent);
if (messageContent) {
const companies = parseApiResponse(messageContent);
resultCount = companies.length;
currentSearchData = companies;
companies.forEach((company, index) => {
cards += generateCompanyCard(company, index + 1);
});
}
}
} else if (data && data.success && data.content) {
// Handle single object response
const messageContent = data.content.originalMessage || data.content.message;
if (messageContent) {
const companies = parseApiResponse(messageContent);
resultCount = companies.length;
currentSearchData = companies;
companies.forEach((company, index) => {
cards += generateCompanyCard(company, index + 1);
});
}
}
// Update UI
updateStats(resultCount, responseTime);
addToRecentActivity(query, resultCount);
resultsCount.textContent = `Found ${resultCount} companies matching "${query}"`;
if (cards && resultCount > 0) {
results.innerHTML = cards;
resultsSection.classList.remove('hidden');
showToast(`Found ${resultCount} companies!`, 'success');
} else {
results.innerHTML = `
<div class="bg-white rounded-lg p-8 text-center border border-gray-200">
<div class="text-4xl mb-3">😔</div>
<p class="text-gray-600 font-semibold">No results found</p>
<p class="text-gray-500 text-sm mt-2">Try a different search term</p>
</div>`;
resultsSection.classList.remove('hidden');
showToast('No results found. Try a different search term.', 'warning');
}
} catch (error) {
console.error('Search error:', error);
let errorMessage = 'Search failed';
let errorDetail = 'Please check your connection and try again';
if (error.message.includes('401')) {
errorMessage = 'Authentication Error';
errorDetail = 'API authentication failed. Please contact support.';
} else if (error.message.includes('404')) {
errorMessage = 'Service Not Found';
errorDetail = 'The search service is currently unavailable.';
} else if (error.message.includes('Failed to fetch')) {
errorMessage = 'Connection Error';
errorDetail = 'Please check your internet connection.';
}
results.innerHTML = `
<div class="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
<div class="text-red-600 text-2xl mb-3">⚠️</div>
<p class="text-red-700 font-semibold">${errorMessage}</p>
<p class="text-red-600 text-sm mt-2">${errorDetail}</p>
<button onclick="location.reload()" class="mt-4 bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg text-sm transition-colors">
Refresh Page
</button>
</div>`;
resultsSection.classList.remove('hidden');
showToast(errorMessage, 'error');
} finally {
searchBtn.innerHTML = '<i class="fas fa-search"></i> <span>Search</span>';
searchBtn.disabled = false;
}
}
// CRUD operation function
async function callCrudOperation() {
const command = document.getElementById('crudCommand').value.trim();
if (!command) {
showToast('Please enter a CRUD command', 'warning');
return;
}
const crudBtn = document.getElementById('crudBtn');
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
crudBtn.disabled = true;
try {
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(command)}`, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
console.log('CRUD result:', result);
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
const clean = raw
.replace(/<[^>]*>/g, '')
.replace(/\*\*/g, '')
.replace(/Complete dataset returned.*included/gi, '')
.trim();
const operation = result?.operation || 'info';
const success = result?.success !== false;
// Show result in modal
showAIModal(`
<div class="space-y-4">
<h4 class="text-lg font-semibold text-gray-800">
${success ? '✅' : '❌'} CRUD Operation Result
</h4>
<div class="bg-${success ? 'green' : 'red'}-50 border border-${success ? 'green' : 'red'}-200 p-4 rounded-lg">
<p class="text-${success ? 'green' : 'red'}-700">${clean}</p>
</div>
</div>
`);
// Show toast notification
if (success) {
showToast('Operation completed successfully!', 'success');
} else {
showToast('Operation failed. Check the details.', 'error');
}
document.getElementById('crudCommand').value = '';
addToRecentActivity(command, 0, 'CRUD');
} catch (error) {
console.error('CRUD error:', error);
showAIModal(`
<div class="space-y-4">
<h4 class="text-lg font-semibold text-gray-800">❌ Operation Failed</h4>
<div class="bg-red-50 border border-red-200 p-4 rounded-lg">
<p class="text-red-700">Error: ${error.message}</p>
</div>
</div>
`);
showToast('CRUD operation failed', 'error');
} finally {
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
crudBtn.disabled = false;
}
}
// Fixed Parse API response function
function parseApiResponse(responseText) {
let companies = [];
try {
// Clean up the text and remove markdown/HTML formatting
let cleanText = responseText
.replace(/<[^>]*>/g, '') // Remove HTML tags
.replace(/\*\*/g, '') // Remove bold markdown
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Convert markdown links to plain text
.replace(/`([^`]+)`/g, '$1') // Remove code formatting
.replace(/🔍.*?🚢/g, '') // Remove search header with ship emoji
.replace(/🔍.*?📦/g, '') // Remove search header with package emoji
.replace(/📊.*?────+/g, '') // Remove stats line
.replace(/💡.*$/gm, '') // Remove suggestions
.trim();
console.log('Cleaned text:', cleanText.substring(0, 500));
// Split by numbered company entries (1. 2. 3. etc.)
const splitPattern = /\b(\d+)\.\s+/;
const parts = cleanText.split(splitPattern);
// Extract company blocks (every 3rd element after split)
const companyBlocks = [];
for (let i = 2; i < parts.length; i += 2) {
if (parts[i] && parts[i].trim()) {
companyBlocks.push(parts[i].trim());
}
}
console.log('Company blocks found:', companyBlocks.length);
companyBlocks.forEach((block, index) => {
if (!block || block.trim().length === 0) return;
console.log(`Processing block ${index + 1}:`, block.substring(0, 200));
const lines = block.split('\n').map(line => line.trim()).filter(line => line.length > 0);
// Extract company name (first line)
const companyName = lines[0] || '';
// Extract phone numbers (look for ☎️ emoji followed by numbers)
const phoneMatches = block.match(/☎️\s*([\d\s\-\(\)\.E\+]+)/g) || [];
const phone = phoneMatches[0] ? phoneMatches[0].replace(/☎️\s*/, '').trim() : '';
// Extract emails (look for 📧 emoji followed by email)
const emailMatches = block.match(/📧\s*([^\s\n]+@[^\s\n]+)/g) || [];
const email = emailMatches[0] ? emailMatches[0].replace(/📧\s*/, '').trim() : '';
// Fixed website extraction
const websiteMatches = block.match(/🔗\s*(https?:\/\/[^\s\n]+)/g) || [];
const website = websiteMatches[0] ? websiteMatches[0].replace(/🔗\s*/, '').replace(/\/$/, '') : '';
// Extract location (look for 📍 emoji followed by location)
const locationMatches = block.match(/📍\s*([^\n🔗📧☎📦🏷]+)/g) || [];
let location = 'Unknown';
if (locationMatches.length > 0) {
location = locationMatches[0].replace(/📍\s*/, '').trim();
}
// Fallback location detection
if (location === 'Unknown' || location.length < 2) {
if (block.includes('United Kingdom')) location = 'United Kingdom';
else if (block.includes('India')) location = 'India';
else if (block.includes('USA') || block.includes('United States')) location = 'USA';
else if (block.includes('UAE') || block.includes('Dubai')) location = 'UAE';
else if (block.includes('China')) location = 'China';
}
// Extract products (look for 📦 emoji)
const productMatches = block.match(/📦\s*([^\n🏷]+)/g) || [];
const products = [];
productMatches.forEach(match => {
const productText = match.replace(/📦\s*/, '').trim();
const productList = productText.split(/[,&]/).map(p => p.trim()).filter(p => p.length > 0);
products.push(...productList);
});
// Extract brands (look for 🏷️ emoji)
const brandMatches = block.match(/🏷️\s*([^\n]+)/g) || [];
const brands = [];
brandMatches.forEach(match => {
const brandText = match.replace(/🏷️\s*/, '').trim();
const brandList = brandText.split(/[,&]/).map(b => b.trim()).filter(b => b.length > 0);
brands.push(...brandList);
});
// Combine products and brands
const allProducts = [...products, ...brands].slice(0, 8);
if (companyName && companyName.length > 0) {
companies.push({
name: companyName,
phone: phone,
email: email,
website: website,
address: location,
products: allProducts
});
}
});
console.log('Final parsed companies:', companies);
} catch (error) {
console.error('Error parsing API response:', error);
console.log('Raw response text:', responseText);
}
return companies;
}
// Generate company card
function generateCompanyCard(company, index) {
const location = company.address?.toLowerCase() || '';
let flagHtml = '';
if (location.includes('india')) {
flagHtml = '<div class="w-8 h-6 bg-gradient-to-b from-orange-400 via-white to-green-500 rounded border border-gray-300 relative"><div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-4 h-4 border-2 border-blue-800 rounded-full"></div></div>';
} else if (location.includes('united kingdom') || location.includes('uk')) {
flagHtml = '<div class="w-8 h-6 bg-blue-800 rounded border border-gray-300 relative"><div class="absolute inset-0 bg-gradient-to-br from-red-600 via-white to-red-600" style="clip-path: polygon(0 0, 100% 0, 0 100%)"></div><div class="absolute inset-0 bg-gradient-to-bl from-red-600 via-white to-red-600" style="clip-path: polygon(100% 0, 100% 100%, 0 100%)"></div><div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-1 h-full bg-red-600"></div><div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full h-1 bg-red-600"></div></div>';
} else if (location.includes('usa') || location.includes('united states')) {
flagHtml = '<div class="w-8 h-6 bg-red-600 rounded border border-gray-300 relative"><div class="absolute top-0 left-0 w-3 h-3 bg-blue-800"></div><div class="absolute top-1 left-0 w-full h-0.5 bg-white"></div><div class="absolute top-2 left-0 w-full h-0.5 bg-white"></div></div>';
} else if (location.includes('uae') || location.includes('dubai')) {
flagHtml = '<div class="w-8 h-6 bg-gradient-to-b from-green-600 via-white via-black to-red-600 rounded border border-gray-300"></div>';
} else if (location.includes('china')) {
flagHtml = '<div class="w-8 h-6 bg-red-600 rounded border border-gray-300 relative"><div class="absolute top-1 left-1 text-yellow-400 text-xs">⭐</div></div>';
} else {
flagHtml = '<div class="w-8 h-6 bg-gray-300 rounded border border-gray-300 flex items-center justify-center text-xs">🌍</div>';
}
return `
<div class="bg-white rounded-lg p-4 border border-gray-200 hover:shadow-md transition-all duration-300 card-hover">
<div class="flex items-center gap-3 mb-3">
${flagHtml}
<h3 class="text-base font-bold text-blue-700 hover:text-blue-800 cursor-pointer line-clamp-2">${company.name}</h3>
</div>
<div class="space-y-2 text-sm text-gray-700">
${company.phone ? `
<div class="flex items-center gap-2">
<i class="fas fa-phone text-green-600 w-4"></i>
<span class="break-all">${company.phone}</span>
</div>
` : ''}
${company.email ? `
<div class="flex items-center gap-2">
<i class="fas fa-envelope text-blue-600 w-4"></i>
<a href="mailto:${company.email}" class="text-blue-600 hover:text-blue-800 break-all">${company.email}</a>
</div>
` : ''}
${company.website ? `
<div class="flex items-center gap-2">
<i class="fas fa-globe text-purple-600 w-4"></i>
<a href="${company.website}" class="text-blue-600 hover:text-blue-800 underline break-all" target="_blank" rel="noopener">${company.website}</a>
</div>
` : ''}
${company.address ? `
<div class="flex items-start gap-2">
<i class="fas fa-map-marker-alt text-red-600 w-4 mt-0.5"></i>
<span class="text-gray-600">${company.address}</span>
</div>
` : ''}
</div>
${company.products && company.products.length > 0 ? `
<div class="mt-3 flex flex-wrap gap-1">
${company.products.slice(0, 3).map(product => `
<span class="bg-blue-50 text-blue-700 px-2 py-1 rounded-full text-xs font-medium">${product}</span>
`).join('')}
${company.products.length > 3 ? `
<span class="bg-gray-100 text-gray-600 px-2 py-1 rounded-full text-xs font-medium">+${company.products.length - 3} more</span>
` : ''}
</div>
` : ''}
<div class="mt-3 pt-3 border-t border-gray-100 flex gap-2">
<button onclick="viewCompanyDetails('${company.name.replace(/'/g, "\\'")}', ${index})" class="flex-1 bg-blue-600 text-white py-2 px-3 rounded-lg hover:bg-blue-700 transition-colors text-xs">
View Details
</button>
<button onclick="saveCompany('${company.name.replace(/'/g, "\\'")}', ${index})" class="px-3 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors text-xs">
<i class="fas fa-bookmark"></i>
</button>
<button onclick="shareCompany('${company.name.replace(/'/g, "\\'")}', ${index})" class="px-3 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors text-xs">
<i class="fas fa-share"></i>
</button>
</div>
</div>
`;
}
// Update stats
function updateStats(resultCount, responseTime) {
totalSearchCount++;
totalResultCount += resultCount;
// Desktop stats
document.getElementById('totalSearches').textContent = totalSearchCount;
document.getElementById('totalResults').textContent = totalResultCount;
document.getElementById('responseTime').textContent = responseTime + 'ms';
// Mobile stats
document.getElementById('mobileSearches').textContent = totalSearchCount;
document.getElementById('mobileResults').textContent = totalResultCount;
document.getElementById('mobileResponseTime').textContent = responseTime + 'ms';
updateLastUpdated();
}
// Add to recent activity
function addToRecentActivity(query, resultCount, type = 'Search') {
const recentActivity = document.getElementById('recentActivity');
const time = new Date().toLocaleTimeString();
// Clear the default message if it exists
if (recentActivity.children.length === 1 && recentActivity.textContent.includes('Start searching')) {
recentActivity.innerHTML = '';
}
const activityItem = document.createElement('div');
activityItem.className = 'flex items-start gap-3 p-2 bg-gray-50 rounded-lg fade-in';
const icon = type === 'CRUD' ? 'fa-terminal' : 'fa-search';
const color = type === 'CRUD' ? 'purple' : 'blue';
activityItem.innerHTML = `
<div class="w-6 h-6 bg-${color}-100 rounded flex items-center justify-center flex-shrink-0 mt-1">
<i class="fas ${icon} text-${color}-600 text-xs"></i>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm text-gray-800 truncate">${query}</p>
<p class="text-xs text-gray-500">${time}${type === 'CRUD' ? 'Operation' : resultCount + ' results'}</p>
</div>
`;
// Insert at the beginning and remove old items
recentActivity.insertBefore(activityItem, recentActivity.firstChild);
while (recentActivity.children.length > 5) {
recentActivity.removeChild(recentActivity.lastChild);
}
}
// Export functionality
function exportResults() {
if (currentSearchData.length === 0) {
showToast('No data to export', 'warning');
return;
}
const headers = ['Company Name', 'Phone', 'Email', 'Website', 'Address', 'Products'];
const csvContent = [
headers.join(','),
...currentSearchData.map(company => [
`"${company.name}"`,
`"${company.phone || ''}"`,
`"${company.email || ''}"`,
`"${company.website || ''}"`,
`"${company.address || ''}"`,
`"${(company.products || []).join('; ')}"`
].join(','))
].join('\n');
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `trade_search_results_${new Date().toISOString().split('T')[0]}.csv`;
a.click();
window.URL.revokeObjectURL(url);
showToast('CSV file downloaded successfully!', 'success');
}
// Clear results
function clearResults() {
document.getElementById('resultsSection').classList.add('hidden');
document.getElementById('emptyState').classList.remove('hidden');
currentSearchData = [];
showToast('Results cleared', 'info');
}
// Company interaction functions
function viewCompanyDetails(companyName, index) {
const company = currentSearchData[index - 1];
if (company) {
showAIModal(`
<div class="space-y-4">
<h4 class="text-xl font-bold text-gray-800">${company.name}</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
${company.phone ? `
<div class="bg-green-50 p-3 rounded-lg border border-green-200">
<h5 class="font-medium text-green-700">Phone</h5>
<p class="text-green-600">${company.phone}</p>
</div>
` : ''}
${company.email ? `
<div class="bg-blue-50 p-3 rounded-lg border border-blue-200">
<h5 class="font-medium text-blue-700">Email</h5>
<p class="text-blue-600">${company.email}</p>
</div>
` : ''}
${company.website ? `
<div class="bg-purple-50 p-3 rounded-lg border border-purple-200">
<h5 class="font-medium text-purple-700">Website</h5>
<p class="text-purple-600">${company.website}</p>
</div>
` : ''}
${company.address ? `
<div class="bg-gray-50 p-3 rounded-lg border border-gray-200">
<h5 class="font-medium text-gray-700">Location</h5>
<p class="text-gray-600">${company.address}</p>
</div>
` : ''}
</div>
${company.products && company.products.length > 0 ? `
<div class="bg-blue-50 p-3 rounded-lg border border-blue-200">
<h5 class="font-medium text-blue-700 mb-2">Products & Services</h5>
<div class="flex flex-wrap gap-2">
${company.products.map(product => `
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded-full text-sm">${product}</span>
`).join('')}
</div>
</div>
` : ''}
</div>
`);
}
}
function saveCompany(companyName, index) {
showToast(`${companyName} saved to favorites!`, 'success');
}
function shareCompany(companyName, index) {
const company = currentSearchData[index - 1];
if (navigator.share && company) {
navigator.share({
title: company.name,
text: `Check out this company: ${company.name}`,
url: window.location.href
});
} else {
const shareText = `${company.name}\n${company.email || ''}\n${company.phone || ''}`;
navigator.clipboard.writeText(shareText).then(() => {
showToast('Company info copied to clipboard!', 'success');
});
}
}
// Toast notification system
function showToast(message, type = 'info') {
const colors = {
success: 'bg-green-100 border-green-300 text-green-800',
error: 'bg-red-100 border-red-300 text-red-800',
warning: 'bg-yellow-100 border-yellow-300 text-yellow-800',
info: 'bg-blue-100 border-blue-300 text-blue-800'
};
const icons = {
success: 'fa-check-circle',
error: 'fa-times-circle',
warning: 'fa-exclamation-triangle',
info: 'fa-info-circle'
};
const toast = document.createElement('div');
toast.className = `toast ${colors[type]} border rounded-lg shadow-lg p-4`;
toast.innerHTML = `
<div class="flex items-center gap-3">
<i class="fas ${icons[type]} text-lg"></i>
<span class="font-medium">${message}</span>
<button onclick="this.parentElement.parentElement.remove()" class="ml-auto text-lg hover:opacity-70">×</button>
</div>
`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 5000);
}
// Modal functions
function showAIModal(content) {
document.getElementById('aiResponse').innerHTML = content;
document.getElementById('aiModal').classList.remove('hidden');
}
function closeAIModal() {
document.getElementById('aiModal').classList.add('hidden');
}
// Mobile CRUD toggle function
function toggleMobileCrud() {
const content = document.getElementById('mobileCrudContent');
const chevron = document.getElementById('crudChevron');
if (content.classList.contains('open')) {
content.classList.remove('open');
chevron.style.transform = 'rotate(0deg)';
} else {
content.classList.add('open');
chevron.style.transform = 'rotate(180deg)';
}
}
// Mobile CRUD operation function
async function callMobileCrudOperation() {
const command = document.getElementById('mobileCrudCommand').value.trim();
if (!command) {
showToast('Please enter a CRUD command', 'warning');
return;
}
const crudBtn = document.getElementById('mobileCrudBtn');
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
crudBtn.disabled = true;
try {
// Use your existing CRUD logic here
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(command)}`, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
console.log('Mobile CRUD result:', result);
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
const clean = raw.replace(/<[^>]*>/g, '').replace(/\*\*/g, '').trim();
showToast('Operation completed successfully!', 'success');
document.getElementById('mobileCrudCommand').value = '';
// Auto-collapse after successful operation
document.getElementById('mobileCrudContent').classList.remove('open');
document.getElementById('crudChevron').style.transform = 'rotate(0deg)';
} catch (error) {
console.error('Mobile CRUD error:', error);
showToast('CRUD operation failed', 'error');
} finally {
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
crudBtn.disabled = false;
}
}
// Make functions globally available
window.callWorkflow = callWorkflow;
window.callCrudOperation = callCrudOperation;
window.setQuickSearch = setQuickSearch;
window.quickSearch = quickSearch;
window.exportResults = exportResults;
window.clearResults = clearResults;
window.viewCompanyDetails = viewCompanyDetails;
window.saveCompany = saveCompany;
window.shareCompany = shareCompany;
window.showAIModal = showAIModal;
window.closeAIModal = closeAIModal;
window.showToast = showToast;
window.goHome = goHome;
window.toggleMobileCrud = toggleMobileCrud;
window.callMobileCrudOperation = callMobileCrudOperation;
</script>
</body>
</html>