From 28467c4ce2993b4f8e352476f6ba413f6fbdf5da Mon Sep 17 00:00:00 2001 From: thecyberlearn Date: Sun, 8 Jun 2025 23:10:23 +0530 Subject: [PATCH] Victor AI - Local code complete --- assets/css/styles.css | 813 +++++++++++++++++++++++ assets/js/calendar-agent.js | 198 ++++++ assets/js/email-agent.js | 546 ++++++++++++++++ assets/js/index.js | 177 +++++ assets/js/navigation.js | 45 ++ assets/js/task-agent.js | 887 +++++++++++++++++++++++++ assets/js/trade-intelligence.js | 1066 +++++++++++++++++++++++++++++++ assets/js/utils.js | 634 ++++++++++++++++++ calendar-agent.html | 400 ++++++++++++ email-agent.html | 253 ++++++++ index.html | 250 ++++++++ logo.png | Bin 0 -> 7435 bytes logo1.png | Bin 0 -> 6421 bytes task-agent.html | 477 ++++++++++++++ trade-intelligence.html | 504 +++++++++++++++ 15 files changed, 6250 insertions(+) create mode 100644 assets/css/styles.css create mode 100644 assets/js/calendar-agent.js create mode 100644 assets/js/email-agent.js create mode 100644 assets/js/index.js create mode 100644 assets/js/navigation.js create mode 100644 assets/js/task-agent.js create mode 100644 assets/js/trade-intelligence.js create mode 100644 assets/js/utils.js create mode 100644 calendar-agent.html create mode 100644 email-agent.html create mode 100644 index.html create mode 100644 logo.png create mode 100644 logo1.png create mode 100644 task-agent.html create mode 100644 trade-intelligence.html diff --git a/assets/css/styles.css b/assets/css/styles.css new file mode 100644 index 0000000..5f4afe5 --- /dev/null +++ b/assets/css/styles.css @@ -0,0 +1,813 @@ +/* Common Styles for AI Services Hub */ + +/* Import fonts */ +@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap'); + +/* Reset and base styles */ +* { + box-sizing: border-box; +} + +body { + font-family: 'Plus Jakarta Sans', sans-serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + margin: 0; + padding: 0; +} + +/* Layout utilities */ +.container { + max-width: 7xl; + margin: 0 auto; + padding: 0 1rem; +} + +@media (min-width: 640px) { + .container { + padding: 0 1.5rem; + } +} + +@media (min-width: 1024px) { + .container { + padding: 0 2rem; + } +} + +/* Component styles */ +.glass-effect { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.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); +} + +.service-card { + background: white; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + border: 1px solid #e5e7eb; +} + +.service-card:hover { + transform: translateY(-8px); + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15); +} + +/* Custom scrollbar */ +.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 */ +.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; } +} + +/* Animations */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes fadeLine { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes float { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-10px); } +} + +@keyframes pulse-glow { + 0%, 100% { box-shadow: 0 0 20px rgba(102, 126, 234, 0.4); } + 50% { box-shadow: 0 0 30px rgba(102, 126, 234, 0.8); } +} + +@keyframes pulse { + 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); } + 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); } + 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); } +} + +.animate-pulse-soft { + animation: pulse-soft 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +@keyframes pulse-soft { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.7; } +} + +.fade-in { + animation: fadeIn 0.4s ease-out; +} + +.animate-slide-up { + animation: slideUp 0.4s ease-out; +} + +.animate-fade { + animation: fadeLine 0.6s ease-in; +} + +.float-animation { + animation: float 3s ease-in-out infinite; +} + +.pulse-glow { + animation: pulse-glow 2s ease-in-out infinite; +} + +/* Status indicator */ +.status-indicator { + position: relative; +} + +.status-indicator::before { + content: ''; + position: absolute; + top: -2px; + right: -2px; + width: 12px; + height: 12px; + background: #10b981; + border-radius: 50%; + border: 2px solid white; + animation: pulse 2s infinite; +} + +/* CONSOLIDATED MOBILE STYLES - REPLACE ALL EXISTING MOBILE CSS */ + +/* Mobile responsive utilities */ +@media (max-width: 768px) { + .mobile-stack { + flex-direction: column; + } + + .mobile-full { + width: 100%; + } + + .mobile-stats-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 0.5rem; + } + + .service-grid { + grid-template-columns: 1fr; + gap: 1.5rem; + } + + /* Header Section */ + .text-center.mb-8 h1 { + font-size: 2rem !important; + line-height: 1.2; + margin-bottom: 1rem; + } + + /* Enhanced Search Command Center */ + .bg-white.rounded-2xl.p-6.mb-8 { + padding: 1rem !important; + margin-bottom: 1.5rem !important; + } + + /* Trade AI Command Header */ + .flex.items-center.gap-3.mb-4 h2 { + font-size: 1.125rem !important; + } + + /* Search Input */ + #query { + font-size: 16px !important; + padding: 0.875rem !important; + } + + /* Search Button */ + .bg-blue-600.hover\:bg-blue-700 { + padding: 0.875rem 1.5rem !important; + min-width: auto !important; + } + + /* Search button inline with text input - only lens icon */ + .flex.flex-col.sm\:flex-row.gap-3.mb-4 { + flex-direction: row !important; + gap: 0.75rem !important; + align-items: stretch !important; + } + + .flex.flex-col.sm\:flex-row.gap-3.mb-4 .flex-1 { + flex: 1 !important; + } + + .flex.flex-col.sm\:flex-row.gap-3.mb-4 button { + width: auto !important; + min-width: 50px !important; + padding: 0.875rem 1rem !important; + flex-shrink: 0 !important; + } + + /* Hide search button text on mobile, show only icon */ + .flex.flex-col.sm\:flex-row.gap-3.mb-4 button span { + display: none !important; + } + + .flex.flex-col.sm\:flex-row.gap-3.mb-4 button i { + margin-right: 0 !important; + font-size: 1rem !important; + } + + /* Quick searches in single row with horizontal scroll */ + .flex.flex-wrap.gap-2 { + flex-direction: row !important; + flex-wrap: nowrap !important; + overflow-x: auto !important; + overflow-y: hidden !important; + align-items: center !important; + padding: 0.5rem 0 !important; + margin-bottom: 0.75rem !important; + -webkit-overflow-scrolling: touch !important; + scrollbar-width: none !important; + -ms-overflow-style: none !important; + gap: 0.5rem !important; + position: relative !important; + } + + /* Hide scrollbar on mobile */ + .flex.flex-wrap.gap-2::-webkit-scrollbar { + display: none !important; + } + + /* Quick searches label */ + .flex.flex-wrap.gap-2 span.text-sm.text-gray-500 { + flex-shrink: 0 !important; + white-space: nowrap !important; + margin-right: 0.5rem !important; + font-size: 0.875rem !important; + color: #6b7280 !important; + align-self: center !important; + } + + /* Quick search buttons mobile */ + .flex.flex-wrap.gap-2 button { + flex-shrink: 0 !important; + white-space: nowrap !important; + font-size: 0.75rem !important; + padding: 0.5rem 0.75rem !important; + min-width: fit-content !important; + width: auto !important; + } + + /* Advanced Filters Section */ + .border-t.border-gray-200.pt-4 { + padding-top: 1rem !important; + } + + /* Advanced Filters Grid */ + .grid.grid-cols-1.md\:grid-cols-3.gap-4 { + grid-template-columns: 1fr !important; + gap: 0.75rem !important; + } + + /* Form Labels */ + .block.text-sm.font-medium.text-gray-700.mb-2 { + margin-bottom: 0.5rem !important; + font-size: 0.875rem !important; + } + + /* Form Inputs and Selects */ + .w-full.px-3.py-2 { + padding: 0.75rem !important; + font-size: 16px !important; + } + + /* Advanced Search Buttons - Mobile Override */ + .flex.flex-wrap.gap-3 { + display: grid !important; + grid-template-columns: 1fr 1fr !important; + gap: 0.75rem !important; + } + + /* Hide Reset button on mobile */ + .flex.flex-wrap.gap-3 button:nth-child(3) { + display: none !important; + } + + /* Style remaining buttons */ + .flex.flex-wrap.gap-3 button:nth-child(1), + .flex.flex-wrap.gap-3 button:nth-child(2) { + width: 100% !important; + justify-content: center !important; + padding: 0.75rem 0.5rem !important; + font-size: 0.875rem !important; + } + + /* Main Content Grid - Stack on mobile */ + .grid.grid-cols-1.lg\:grid-cols-3.gap-6 { + grid-template-columns: 1fr !important; + gap: 1rem !important; + } + + /* Results Section */ + .lg\:col-span-2 { + grid-column: span 1 !important; + } + + /* Search Results Container */ + #resultsSection .bg-white.rounded-2xl { + padding: 1rem !important; + margin-bottom: 1rem !important; + } + + /* Search Results Header */ + #resultsSection .flex.items-center.justify-between { + flex-direction: column !important; + align-items: flex-start !important; + gap: 1rem !important; + margin-bottom: 1rem !important; + } + + #resultsSection .flex.items-center.justify-between h3 { + font-size: 1.25rem !important; + margin-bottom: 0 !important; + } + + /* Results Count and Controls */ + #resultsSection .flex.items-center.gap-2 { + width: 100% !important; + justify-content: space-between !important; + flex-wrap: wrap !important; + gap: 0.5rem !important; + } + + #resultsCount { + font-size: 0.875rem !important; + color: #059669 !important; + background: #ecfdf5 !important; + padding: 0.25rem 0.5rem !important; + border-radius: 0.375rem !important; + border: 1px solid #a7f3d0 !important; + font-weight: 500 !important; + order: 1 !important; + width: 100% !important; + } + + /* Sort Dropdown */ + #sortBy { + font-size: 0.875rem !important; + padding: 0.5rem !important; + order: 2 !important; + flex: 1 !important; + min-width: 120px !important; + background: white !important; + border: 1px solid #d1d5db !important; + border-radius: 0.5rem !important; + font-weight: 500 !important; + } + + #sortBy:focus { + border-color: #3b82f6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; + } + + /* Export Button */ + .bg-gray-100.hover\:bg-gray-200 { + font-size: 0.875rem !important; + padding: 0.5rem 0.75rem !important; + order: 3 !important; + flex-shrink: 0 !important; + background: #f3f4f6 !important; + border: 1px solid #d1d5db !important; + border-radius: 0.5rem !important; + font-weight: 500 !important; + transition: all 0.15s ease !important; + } + + /* ===== SEARCH RESULTS MOBILE STYLING ===== */ + + /* Main search results container */ + #results { + grid-template-columns: 1fr !important; + gap: 1rem !important; + } + + /* Individual result cards - SIMPLE DESKTOP-LIKE LAYOUT */ + #results > div { + border: 1px solid #e5e7eb !important; + border-radius: 0.5rem !important; + padding: 1rem !important; + margin-bottom: 0.75rem !important; + background: #ffffff !important; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06) !important; + transition: all 0.2s ease !important; + } + + /* Company header with flag and name - KEEP DESKTOP LAYOUT */ + #results .flex.items-center.gap-3 { + gap: 0.75rem !important; + margin-bottom: 0.75rem !important; + flex-direction: row !important; + align-items: center !important; + } + + /* Flag element */ + #results .w-8.h-6 { + flex-shrink: 0 !important; + width: 2rem !important; + height: 1.5rem !important; + } + + /* Company name */ + #results .text-base.font-bold, + #results .text-lg.font-bold { + flex: 1 !important; + font-size: 1rem !important; + line-height: 1.3 !important; + word-break: break-word !important; + color: #1f2937 !important; + font-weight: 600 !important; + } + + /* Contact information section - KEEP DESKTOP STRUCTURE */ + #results .space-y-2 { + margin-top: 0.75rem !important; + display: flex !important; + flex-direction: column !important; + gap: 0.5rem !important; + } + + /* Individual contact items - CLEAN ROWS LIKE DESKTOP */ + #results .flex.items-center.gap-2, + #results .flex.items-start.gap-2 { + margin-bottom: 0 !important; + align-items: center !important; + padding: 0.25rem 0 !important; + background: transparent !important; + border: none !important; + border-radius: 0 !important; + box-shadow: none !important; + gap: 0.75rem !important; + } + + /* Icons - ensure they display properly */ + #results .fas, + #results .fa { + width: 1rem !important; + text-align: center !important; + flex-shrink: 0 !important; + margin-right: 0.5rem !important; + font-size: 0.875rem !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + } + + /* Icon colors - same as desktop */ + #results .fa-phone { color: #059669 !important; } + #results .fa-envelope { color: #2563eb !important; } + #results .fa-globe { color: #7c3aed !important; } + #results .fa-map-marker-alt { color: #dc2626 !important; } + + /* Contact text */ + #results .flex.items-center.gap-2 span, + #results .flex.items-center.gap-2 a, + #results .flex.items-start.gap-2 span { + word-break: break-word !important; + line-height: 1.3 !important; + font-size: 0.875rem !important; + flex: 1 !important; + margin: 0 !important; + } + + /* Text colors - same as desktop */ + #results .text-green-600 { color: #059669 !important; } + #results .text-blue-600 { color: #2563eb !important; } + #results .text-purple-600 { color: #7c3aed !important; } + #results .text-gray-600 { color: #4b5563 !important; } + #results .text-blue-700 { color: #1d4ed8 !important; } + + /* Product tags - same as desktop structure */ + #results .flex.flex-wrap.gap-1 { + margin-top: 0.75rem !important; + gap: 0.375rem !important; + padding-top: 0.75rem !important; + border-top: 1px solid #f3f4f6 !important; + } + + /* Product tag styling - same as desktop */ + #results .bg-blue-50, + #results .bg-blue-100 { + font-size: 0.75rem !important; + padding: 0.25rem 0.5rem !important; + background: linear-gradient(135deg, #dbeafe, #bfdbfe) !important; + color: #1e40af !important; + border: 1px solid #93c5fd !important; + border-radius: 9999px !important; + display: inline-block !important; + margin: 0.25rem 0.25rem 0.25rem 0 !important; + font-weight: 500 !important; + } + + /* Hover effects - same as desktop */ + #results > div:hover { + transform: translateY(-2px) !important; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important; + } + + /* Remove hover effects on contact items */ + #results .flex.items-center.gap-2:hover, + #results .flex.items-start.gap-2:hover { + background: transparent !important; + border: none !important; + transform: none !important; + } + + /* Links - ensure they're touch-friendly */ + #results a { + min-height: 32px !important; + display: inline-flex !important; + align-items: center !important; + text-decoration: none !important; + } + + #results a:hover { + opacity: 0.8 !important; + } + + /* CRUD Wrench Button */ + .fixed.bottom-6.right-6 { + bottom: 1rem !important; + right: 1rem !important; + } + + .w-14.h-14 { + width: 3rem !important; + height: 3rem !important; + } + + /* CRUD Modal Mobile Adjustments */ + .max-w-4xl { + max-width: calc(100vw - 2rem) !important; + margin: 1rem !important; + } + + .max-h-\[90vh\] { + max-height: calc(100vh - 2rem) !important; + } + + /* CRUD Modal Content */ + .p-6 { + padding: 1rem !important; + } + + /* CRUD Form Grids */ + .grid.grid-cols-1.md\:grid-cols-2.gap-4 { + grid-template-columns: 1fr !important; + gap: 0.75rem !important; + } + + /* Toast Notifications */ + .fixed.top-4.right-4 { + top: 1rem !important; + right: 1rem !important; + left: 1rem !important; + } + + .max-w-sm { + max-width: none !important; + } +} + +/* Very small screens */ +@media (max-width: 480px) { + #results > div { + padding: 0.875rem !important; + } + + #results .text-base.font-bold, + #results .text-lg.font-bold { + font-size: 0.9375rem !important; + } + + #results .flex.items-center.gap-2 span, + #results .flex.items-center.gap-2 a { + font-size: 0.8125rem !important; + } +} + +/* Tablet and larger screens */ +@media (min-width: 769px) and (max-width: 1024px) { + .service-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 1025px) { + .service-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +/* IMMEDIATE MOBILE FIXES - Add to end of styles.css */ + +@media (max-width: 768px) { + /* Fix search button staying inline with input */ + .flex.flex-col.sm\:flex-row.gap-3.mb-4 { + flex-direction: row !important; + align-items: stretch !important; + gap: 0.5rem !important; + } + + .flex.flex-col.sm\:flex-row.gap-3.mb-4 .flex-1 { + flex: 1 !important; + } + + .flex.flex-col.sm\:flex-row.gap-3.mb-4 button { + width: auto !important; + min-width: 50px !important; + flex-shrink: 0 !important; + padding: 0.875rem 1rem !important; + } + + /* Hide button text on mobile, show only icon */ + .flex.flex-col.sm\:flex-row.gap-3.mb-4 button span { + display: none !important; + } + + /* Fix advanced search buttons */ + .flex.flex-wrap.gap-3 { + display: grid !important; + grid-template-columns: 1fr 1fr !important; + gap: 0.5rem !important; + } + + /* Hide reset button on mobile for cleaner look */ + .flex.flex-wrap.gap-3 button:nth-child(3) { + grid-column: span 2 !important; + font-size: 0.8rem !important; + } + + /* Fix modal sizing */ + .max-w-4xl, .max-w-2xl { + max-width: calc(100vw - 1rem) !important; + margin: 0.5rem !important; + } + + /* Improve touch targets */ + button, .cursor-pointer { + min-height: 44px !important; + } +} + +/* Very small screens (phones in portrait) */ +@media (max-width: 480px) { + .px-4 { + padding-left: 0.75rem !important; + padding-right: 0.75rem !important; + } + + .text-3xl, .text-4xl { + font-size: 1.5rem !important; + line-height: 1.3 !important; + } + + /* Stack stats in 2x2 grid instead of 4x1 */ + .grid.grid-cols-2.lg\:grid-cols-4 { + grid-template-columns: repeat(2, 1fr) !important; + gap: 0.5rem !important; + } +} + +/* Searchable Country Dropdown Styles - Add to end of styles.css */ + +/* Input with search icon */ +.relative input[id$="_input"] { + padding-right: 2.5rem !important; +} + +/* Search icon positioning */ +.relative .absolute.right-3 { + pointer-events: none; + z-index: 5; +} + +/* Dropdown menu */ +.relative .absolute.z-20 { + border: 1px solid #d1d5db; + border-radius: 0.5rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + background: white; + max-height: 12rem; + overflow-y: auto; + margin-top: 0.25rem; +} + +/* Dropdown options */ +.relative .absolute.z-20 > div { + padding: 0.75rem; + cursor: pointer; + font-size: 0.875rem; + border-bottom: 1px solid #f3f4f6; + transition: all 0.15s ease; +} + +.relative .absolute.z-20 > div:last-child { + border-bottom: none; +} + +.relative .absolute.z-20 > div:hover { + background-color: #eff6ff; + color: #1d4ed8; +} + +/* Highlighted option (keyboard navigation) */ +.relative .absolute.z-20 > div.bg-blue-100 { + background-color: #dbeafe !important; + color: #1e40af !important; +} + +/* Custom scrollbar for dropdown */ +.relative .absolute.z-20::-webkit-scrollbar { + width: 6px; +} + +.relative .absolute.z-20::-webkit-scrollbar-track { + background: #f1f5f9; + border-radius: 3px; +} + +.relative .absolute.z-20::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 3px; +} + +.relative .absolute.z-20::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +/* Mobile improvements */ +@media (max-width: 768px) { + .relative input[id$="_input"] { + font-size: 16px !important; /* Prevents zoom on iOS */ + padding: 0.875rem 2.5rem 0.875rem 0.75rem !important; + } + + .relative .absolute.z-20 > div { + padding: 1rem 0.75rem; + min-height: 44px; /* Better touch targets */ + display: flex; + align-items: center; + } + + .relative .absolute.z-20 { + max-height: 10rem; /* Smaller on mobile */ + } +} + +/* Focus styles for accessibility */ +.relative input[id$="_input"]:focus { + outline: none !important; + border-color: #3b82f6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; +} + +/* Animation for icon change */ +.relative .absolute.right-3 i { + transition: all 0.2s ease; +} \ No newline at end of file diff --git a/assets/js/calendar-agent.js b/assets/js/calendar-agent.js new file mode 100644 index 0000000..39a1896 --- /dev/null +++ b/assets/js/calendar-agent.js @@ -0,0 +1,198 @@ +/** + * Calendar Agent Specific Functions + */ + +class CalendarAgent { + constructor() { + this.totalActions = 0; + this.init(); + } + + init() { + Utils.time.updateLastUpdated('lastSync'); + this.initializeEventListeners(); + } + + initializeEventListeners() { + Utils.events.onEnterKey('calendarCommand', () => this.executeCalendarCommand()); + } + + setCalendarCommand(command) { + Utils.form.setInputValue('calendarCommand', command); + this.executeCalendarCommand(); + } + + async executeCalendarCommand() { + const command = Utils.form.getInputValue('calendarCommand'); + if (!command) { + Utils.toast.warning('Please enter a command'); + return; + } + + Utils.button.setLoading('calendarAiBtn', 'Processing...', 'fa-spinner fa-spin'); + + try { + // Simulate AI processing + await new Promise(resolve => setTimeout(resolve, 1500)); + + const response = this.generateCalendarAIResponse(command); + Utils.modal.showAI(response); + + this.updateCalendarStats(); + Utils.time.updateLastUpdated('lastSync'); + + } catch (error) { + Utils.toast.error('Error processing command: ' + error.message); + } finally { + Utils.button.restore('calendarAiBtn', 'Execute', 'fa-robot'); + Utils.form.clearInput('calendarCommand'); + } + } + + generateCalendarAIResponse(command) { + const lowerCommand = command.toLowerCase(); + + if (lowerCommand.includes('schedule') && lowerCommand.includes('meeting')) { + return ` +
+

📅 Schedule Meeting

+
+
✅ Optimal Time Found
+

Best time for all attendees: Tomorrow 3:30 PM - 4:30 PM

+
+
+ + 5 attendees available +
+
+ + Conference Room B reserved +
+
+ + Zoom link generated +
+
+
+
+
📋 Meeting Details
+
+

Title: Team Strategy Discussion

+

Duration: 1 hour

+

Attendees: Sarah, John, Mike, Lisa, David

+

Agenda: Q1 planning and resource allocation

+
+
+
+ + +
+
+ `; + } + + return ` +
+

🤖 Calendar AI Response

+
+

Processing your request: "${command}"

+

I can help you with:

+
    +
  • • Smart meeting scheduling and optimization
  • +
  • • Finding optimal free time slots
  • +
  • • Conflict detection and resolution
  • +
  • • Focus time blocking and protection
  • +
  • • Calendar analysis and insights
  • +
+
+
+ `; + } + + updateCalendarStats() { + Utils.stats.increment('aiScheduled'); + this.totalActions++; + } + + // Quick action functions + scheduleMeeting() { + Utils.modal.showAI(` +
+

📅 Schedule New Meeting

+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+ + +
+
+ +
+
+ `); + } + + findFreeTime() { + this.updateCalendarStats(); + Utils.toast.info('🔍 Found 3 optimal time slots this week for your requirements'); + } + + blockFocusTime() { + this.updateCalendarStats(); + Utils.toast.success('🧠 Protected 2-hour focus block scheduled for tomorrow 10-12 AM'); + } + + optimizeSchedule() { + if (confirm('Let AI optimize your schedule for maximum productivity?')) { + this.updateCalendarStats(); + Utils.toast.success('✨ Schedule optimized! Moved 3 meetings to create better focus blocks and reduced meeting fragmentation by 40%.'); + } + } +} + +// Initialize Calendar Agent when DOM is loaded +document.addEventListener('DOMContentLoaded', function() { + window.calendarAgent = new CalendarAgent(); + + // Make functions globally available for onclick handlers + window.executeCalendarCommand = () => calendarAgent.executeCalendarCommand(); + window.setCalendarCommand = (cmd) => calendarAgent.setCalendarCommand(cmd); + window.scheduleMeeting = () => calendarAgent.scheduleMeeting(); + window.findFreeTime = () => calendarAgent.findFreeTime(); + window.blockFocusTime = () => calendarAgent.blockFocusTime(); + window.optimizeSchedule = () => calendarAgent.optimizeSchedule(); + window.closeAIModal = () => Utils.modal.hideAI(); + window.goHome = () => Utils.navigation.goHome(); +}); \ No newline at end of file diff --git a/assets/js/email-agent.js b/assets/js/email-agent.js new file mode 100644 index 0000000..303241a --- /dev/null +++ b/assets/js/email-agent.js @@ -0,0 +1,546 @@ +/** + * Email Agent - Webhook Integration + * Connects to your n8n email analysis workflow + */ + +class EmailAgent { + constructor() { + this.apiEndpoint = 'https://thecyberlearn.app.n8n.cloud/webhook/email-agent'; + this.currentEmails = []; + this.currentFilter = 'all'; + this.totalActions = 0; + this.init(); + } + + init() { + this.updateLastSync(); + this.initializeEventListeners(); + this.loadEmails(); // Auto-load emails on page load + } + + initializeEventListeners() { + // Search on Enter key + const searchInput = document.getElementById('searchQuery'); + if (searchInput) { + searchInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + this.searchEmails(); + } + }); + } + } + + updateLastSync() { + const element = document.getElementById('lastSync'); + if (element) { + element.textContent = new Date().toLocaleTimeString(); + } + } + + // Main function to load emails from webhook + async loadEmails(action = 'get_inbox', params = {}) { + const loadingState = document.getElementById('loadingState'); + const emailList = document.getElementById('emailList'); + const emptyState = document.getElementById('emptyState'); + + try { + // Show loading state + this.showLoading(true); + + // Build API URL + const url = new URL(this.apiEndpoint); + url.searchParams.append('action', action); + + // Add additional parameters + Object.keys(params).forEach(key => { + if (params[key]) { + url.searchParams.append(key, params[key]); + } + }); + + console.log('Loading emails from:', url.toString()); + + const response = await fetch(url.toString()); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + console.log('Email data received:', data); + + if (data.success && data.emails) { + this.currentEmails = data.emails; + this.displayEmails(data.emails); + this.updateStats(data); + this.addToRecentActivity(`Loaded ${data.emails.length} emails`, 'load'); + + // Hide empty state, show email list + if (emptyState) emptyState.classList.add('hidden'); + if (emailList) emailList.classList.remove('hidden'); + + } else { + this.showEmptyState(); + console.log('No emails found or API error'); + } + + } catch (error) { + console.error('Error loading emails:', error); + this.showError('Failed to load emails: ' + error.message); + } finally { + this.showLoading(false); + this.updateLastSync(); + } + } + + // Display emails in the UI + displayEmails(emails) { + const emailList = document.getElementById('emailList'); + if (!emailList) return; + + if (emails.length === 0) { + this.showEmptyState(); + return; + } + + const emailHTML = emails.map((email, index) => { + return this.generateEmailCard(email, index); + }).join(''); + + emailList.innerHTML = emailHTML; + emailList.classList.remove('hidden'); + + // Add click handlers + this.attachEmailClickHandlers(); + } + + // Generate individual email card HTML + generateEmailCard(email, index) { + const priorityClass = `priority-${email.priority.toLowerCase()}`; + const unreadClass = email.unread ? 'email-unread' : 'email-read'; + const typeIcon = this.getTypeIcon(email.type); + const typeColor = this.getTypeColor(email.type); + + return ` +
+ +
+
+ +
+ +
+ + +
+
+ ${email.sender} + ${email.priority === 'High' ? 'High Priority' : ''} + ${email.priority === 'Medium' ? 'Medium' : ''} + ${email.unread ? '' : ''} +
+

${email.subject}

+

${email.summary}

+
+
+ + +
+
${email.timestamp}
+
+ ${email.attachments > 0 ? `${email.attachments}` : ''} + ${email.type.toUpperCase()} +
+
+
+ + +
+ + + +
+
+ `; + } + + // Get type icon for email type + getTypeIcon(type) { + const icons = { + 'email': 'fas fa-envelope', + 'pdf': 'fas fa-file-pdf', + 'image': 'fas fa-image' + }; + return icons[type] || 'fas fa-file'; + } + + // Get type color for email type + getTypeColor(type) { + const colors = { + 'email': 'blue', + 'pdf': 'red', + 'image': 'green' + }; + return colors[type] || 'gray'; + } + + // Search emails + async searchEmails() { + const searchQuery = document.getElementById('searchQuery'); + const query = searchQuery ? searchQuery.value.trim() : ''; + + if (!query) { + this.loadEmails(); // Load all if no search query + return; + } + + await this.loadEmails('search', { query: query }); + this.addToRecentActivity(`Searched for "${query}"`, 'search'); + } + + // Filter emails by type + async filterEmails(type) { + this.currentFilter = type; + + // Update filter button styles + document.querySelectorAll('.filter-btn').forEach(btn => { + btn.classList.remove('active', 'bg-blue-100', 'text-blue-700'); + btn.classList.add('bg-gray-100', 'text-gray-700'); + }); + + const activeBtn = document.querySelector(`[data-filter="${type}"]`); + if (activeBtn) { + activeBtn.classList.remove('bg-gray-100', 'text-gray-700'); + activeBtn.classList.add('active', 'bg-blue-100', 'text-blue-700'); + } + + if (type === 'all') { + await this.loadEmails('get_inbox'); + } else { + await this.loadEmails('filter', { type: type }); + } + + this.addToRecentActivity(`Filtered by ${type}`, 'filter'); + } + + // Refresh emails + async refreshEmails() { + await this.loadEmails(this.currentFilter === 'all' ? 'get_inbox' : 'filter', + this.currentFilter === 'all' ? {} : { type: this.currentFilter }); + this.addToRecentActivity('Refreshed emails', 'refresh'); + } + + // View email details in modal + viewEmailDetails(emailId) { + const email = this.currentEmails.find(e => e.id === emailId); + if (!email) return; + + const modal = document.getElementById('emailModal'); + const content = document.getElementById('emailContent'); + + if (!modal || !content) return; + + const detailHTML = ` +
+ +
+
+
+ +
+
+

${email.subject}

+

From: ${email.sender} (${email.senderEmail})

+
+
+ + ${email.priority} Priority + +

${email.timestamp}

+
+
+
+ + +
+
+

AI Summary

+
+

${email.summary}

+
+
+ + ${email.fullSummary && email.fullSummary !== email.summary ? ` +
+

Full Analysis

+
+

${email.fullSummary}

+
+
+ ` : ''} + + +
+
+

AI Analysis

+

Priority: ${email.priority}

+

Attachments: ${email.attachments}

+

Auto-processed: Yes

+
+
+
+
+ `; + + content.innerHTML = detailHTML; + modal.classList.remove('hidden'); + + this.addToRecentActivity(`Viewed "${email.subject}"`, 'view'); + } + + // Close email modal + closeEmailModal() { + const modal = document.getElementById('emailModal'); + if (modal) { + modal.classList.add('hidden'); + } + } + + // Mark email as read/unread (simulated) + markAsRead(emailId) { + const email = this.currentEmails.find(e => e.id === emailId); + if (email) { + email.unread = !email.unread; + this.displayEmails(this.currentEmails); // Refresh display + this.addToRecentActivity(`Marked "${email.subject}" as ${email.unread ? 'unread' : 'read'}`, 'mark'); + } + } + + // Share email (copy to clipboard) + shareEmail(emailId) { + const email = this.currentEmails.find(e => e.id === emailId); + if (email) { + const shareText = `${email.subject}\nFrom: ${email.sender}\nSummary: ${email.summary}`; + + if (navigator.clipboard) { + navigator.clipboard.writeText(shareText).then(() => { + this.showToast('Email details copied to clipboard!', 'success'); + }); + } else { + // Fallback for older browsers + const textArea = document.createElement('textarea'); + textArea.value = shareText; + document.body.appendChild(textArea); + textArea.select(); + document.execCommand('copy'); + document.body.removeChild(textArea); + this.showToast('Email details copied to clipboard!', 'success'); + } + + this.addToRecentActivity(`Shared "${email.subject}"`, 'share'); + } + } + + // Export emails to CSV + exportEmails() { + if (this.currentEmails.length === 0) { + this.showToast('No emails to export', 'warning'); + return; + } + + const headers = ['Date', 'Sender', 'Subject', 'Type', 'Priority', 'Summary']; + const csvContent = [ + headers.join(','), + ...this.currentEmails.map(email => [ + `"${email.date}"`, + `"${email.sender}"`, + `"${email.subject}"`, + `"${email.type}"`, + `"${email.priority}"`, + `"${email.summary.replace(/"/g, '""')}"` + ].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 = `email_summaries_${new Date().toISOString().split('T')[0]}.csv`; + a.click(); + window.URL.revokeObjectURL(url); + + this.showToast('Email data exported successfully!', 'success'); + this.addToRecentActivity(`Exported ${this.currentEmails.length} emails`, 'export'); + } + + // Update statistics + updateStats(data) { + const stats = data.stats || {}; + + // Update stat counters + this.updateElement('totalCount', stats.total || this.currentEmails.length); + this.updateElement('priorityCount', stats.high_priority || 0); + this.updateElement('processedCount', stats.total || this.currentEmails.length); + this.updateElement('aiActionsCount', stats.total || this.currentEmails.length); + } + + // Helper function to update element text + updateElement(id, value) { + const element = document.getElementById(id); + if (element) { + element.textContent = value; + } + } + + // Add activity to recent activity panel + addToRecentActivity(message, type = 'info') { + const recentActivity = document.getElementById('recentActivity'); + if (!recentActivity) return; + + const iconMap = { + 'load': 'fa-download', + 'search': 'fa-search', + 'filter': 'fa-filter', + 'refresh': 'fa-sync-alt', + 'view': 'fa-eye', + 'mark': 'fa-check', + 'share': 'fa-share', + 'export': 'fa-file-export' + }; + + const colorMap = { + 'load': 'blue', + 'search': 'green', + 'filter': 'purple', + 'refresh': 'orange', + 'view': 'indigo', + 'mark': 'green', + 'share': 'blue', + 'export': 'red' + }; + + const icon = iconMap[type] || 'fa-info-circle'; + const color = colorMap[type] || 'blue'; + const time = new Date().toLocaleTimeString(); + + // Clear default message if exists + if (recentActivity.children.length === 1 && recentActivity.textContent.includes('Load emails')) { + recentActivity.innerHTML = ''; + } + + const activityItem = document.createElement('div'); + activityItem.className = 'flex items-start gap-3 p-2 bg-gray-50 rounded-lg fade-in'; + activityItem.innerHTML = ` +
+ +
+
+

${message}

+

${time}

+
+ `; + + recentActivity.insertBefore(activityItem, recentActivity.firstChild); + + // Keep only last 5 activities + while (recentActivity.children.length > 5) { + recentActivity.removeChild(recentActivity.lastChild); + } + + this.totalActions++; + } + + // Show/hide loading state + showLoading(show) { + const loadingState = document.getElementById('loadingState'); + const emailList = document.getElementById('emailList'); + + if (loadingState) { + if (show) { + loadingState.classList.remove('hidden'); + } else { + loadingState.classList.add('hidden'); + } + } + + if (emailList && show) { + emailList.classList.add('hidden'); + } + } + + // Show empty state + showEmptyState() { + const emailList = document.getElementById('emailList'); + const emptyState = document.getElementById('emptyState'); + + if (emailList) emailList.classList.add('hidden'); + if (emptyState) emptyState.classList.remove('hidden'); + } + + // Show error message + showError(message) { + this.showToast(message, 'error'); + this.showEmptyState(); + } + + // Toast notification system + 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 = `fixed bottom-4 right-4 z-50 ${colors[type]} border rounded-lg shadow-lg p-4 max-w-sm`; + toast.innerHTML = ` +
+ + ${message} + +
+ `; + + document.body.appendChild(toast); + setTimeout(() => toast.remove(), 5000); + } + + // Attach click handlers to email cards + attachEmailClickHandlers() { + // Click handlers are already in the HTML via onclick attributes + // This method can be used for additional event handling if needed + } +} + +// Initialize Email Agent when DOM is loaded +document.addEventListener('DOMContentLoaded', function() { + window.emailAgent = new EmailAgent(); + + // Make functions globally available for onclick handlers + window.loadEmails = () => emailAgent.loadEmails(); + window.searchEmails = () => emailAgent.searchEmails(); + window.filterEmails = (type) => emailAgent.filterEmails(type); + window.refreshEmails = () => emailAgent.refreshEmails(); + window.exportEmails = () => emailAgent.exportEmails(); + window.closeEmailModal = () => emailAgent.closeEmailModal(); + +}); \ No newline at end of file diff --git a/assets/js/index.js b/assets/js/index.js new file mode 100644 index 0000000..be475dc --- /dev/null +++ b/assets/js/index.js @@ -0,0 +1,177 @@ +/** + * Main Index Page Functions + */ + +class AIServicesHub { + constructor() { + this.init(); + } + + init() { + this.addAnimations(); + this.initializeEventListeners(); + } + + initializeEventListeners() { + // Service card click handlers are handled via onclick attributes + // But we can add keyboard navigation here if needed + this.addKeyboardNavigation(); + } + + addAnimations() { + // Add staggered animation to service cards + const cards = document.querySelectorAll('.service-card'); + cards.forEach((card, index) => { + card.style.animationDelay = `${index * 0.1}s`; + card.classList.add('opacity-0'); + setTimeout(() => { + card.style.animation = 'fadeInUp 0.6s ease-out forwards'; + card.classList.remove('opacity-0'); + }, index * 100); + }); + } + + addKeyboardNavigation() { + // Add keyboard navigation for service cards + const serviceCards = document.querySelectorAll('.service-card'); + serviceCards.forEach((card, index) => { + card.setAttribute('tabindex', '0'); + card.addEventListener('keypress', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + card.click(); + } + }); + }); + } + + navigateToService(service) { + // Map service names to actual HTML files + const serviceUrls = { + 'trade-intelligence': 'trade-intelligence.html', + 'email-agent': 'email-agent.html', + 'calendar-agent': 'calendar-agent.html', + 'task-agent': 'task-agent.html' + }; + + const url = serviceUrls[service]; + + if (url) { + // Check if file exists by trying to navigate + try { + window.location.href = url; + } catch (error) { + // Fallback to showing information if file doesn't exist + this.showServiceInfo(service); + } + } else { + this.showServiceInfo(service); + } + } + + showServiceInfo(service) { + const services = { + 'trade-intelligence': { + name: 'Trade Intelligence', + description: 'Find exporters, importers, and trade data from around the world', + features: ['50K+ Companies', '150+ Countries', 'Real-time Search', 'Export Data'], + icon: 'fas fa-ship', + color: 'blue' + }, + 'email-agent': { + name: 'Email Agent', + description: 'Smart email management and summarization with AI', + features: ['Email Summarization', 'Priority Detection', 'Auto-categorization', 'Smart Replies'], + icon: 'fas fa-envelope', + color: 'purple' + }, + 'calendar-agent': { + name: 'Calendar Agent', + description: 'Intelligent scheduling and meeting management', + features: ['Smart Scheduling', 'Conflict Detection', 'Meeting Optimization', 'Time Blocking'], + icon: 'fas fa-calendar', + color: 'green' + }, + 'task-agent': { + name: 'Task Agent', + description: 'Smart task management and prioritization', + features: ['AI Prioritization', 'Progress Tracking', 'Deadline Management', 'Workload Balancing'], + icon: 'fas fa-tasks', + color: 'orange' + } + }; + + const serviceData = services[service]; + if (serviceData) { + Utils.modal.show('demoModal', ` +
+
+ +
+

${serviceData.name}

+

${serviceData.description}

+ +
+ ${serviceData.features.map(feature => ` +
+

${feature}

+
+ `).join('')} +
+ +
+

+ Coming Soon!
+ This agent is currently in development. +
Check back soon for the full experience. +

+
+
+ `); + } + } + + showDemo() { + Utils.modal.show('demoModal'); + } + + showDocumentation() { + Utils.modal.show('docModal'); + } + + closeDemoModal() { + Utils.modal.hide('demoModal'); + } + + closeDocModal() { + Utils.modal.hide('docModal'); + } +} + +// Add CSS for fade in animation +const style = document.createElement('style'); +style.textContent = ` + @keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } + } +`; +document.head.appendChild(style); + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', function() { + window.aiServicesHub = new AIServicesHub(); + + // Make functions globally available for onclick handlers + window.navigateToService = (service) => aiServicesHub.navigateToService(service); + window.showDemo = () => aiServicesHub.showDemo(); + window.showDocumentation = () => aiServicesHub.showDocumentation(); + window.closeDemoModal = () => aiServicesHub.closeDemoModal(); + window.closeDocModal = () => aiServicesHub.closeDocModal(); +}); \ No newline at end of file diff --git a/assets/js/navigation.js b/assets/js/navigation.js new file mode 100644 index 0000000..24c2429 --- /dev/null +++ b/assets/js/navigation.js @@ -0,0 +1,45 @@ +/** + * Minimal Navigation Component + * Only creates breadcrumbs for your existing HTML + */ + +document.addEventListener('DOMContentLoaded', function() { + createBreadcrumbs(); +}); + +function createBreadcrumbs() { + const breadcrumbContainer = document.querySelector('.breadcrumbs'); + if (!breadcrumbContainer) return; + + // Detect current page + const path = window.location.pathname; + const filename = path.split('/').pop() || 'index.html'; + const currentPage = filename.replace('.html', ''); + + const pages = { + 'index': { name: 'Home', icon: 'fas fa-home' }, + 'trade-intelligence': { name: 'Trade Intelligence', icon: 'fas fa-ship' }, + 'email-agent': { name: 'Email Agent', icon: 'fas fa-envelope' }, + 'calendar-agent': { name: 'Calendar Agent', icon: 'fas fa-calendar' }, + 'task-agent': { name: 'Task Agent', icon: 'fas fa-tasks' } + }; + + const currentPageInfo = pages[currentPage]; + if (!currentPageInfo) return; + + breadcrumbContainer.innerHTML = ` +
+ + + Home + + ${currentPage !== 'index' ? ` + + + + ${currentPageInfo.name} + + ` : ''} +
+ `; +} \ No newline at end of file diff --git a/assets/js/task-agent.js b/assets/js/task-agent.js new file mode 100644 index 0000000..e356084 --- /dev/null +++ b/assets/js/task-agent.js @@ -0,0 +1,887 @@ +/** + * Task Agent Specific Functions - Fixed Version + */ + +class TaskAgent { + constructor() { + this.totalActions = 0; + this.tasks = []; + this.stats = { + total: 0, + overdue: 0, + dueToday: 0, + completed: 0, + inProgress: 0, + toDo: 0 + }; + // Set default API URL - you can override this + this.apiUrl = 'https://thecyberlearn.app.n8n.cloud/webhook/tasks'; + this.init(); + } + + init() { + // Wait for DOM to be ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => this.initializeAfterDOM()); + } else { + this.initializeAfterDOM(); + } + } + + initializeAfterDOM() { + this.updateLastUpdated('lastUpdate'); + this.initializeEventListeners(); + + // Load sample tasks if API URL is not configured + if (this.apiUrl.includes('your-n8n-instance.com')) { + console.log('Using sample data - configure apiUrl for real data'); + this.loadSampleTasks(); + } else { + this.loadTasks(); // Load tasks from API + } + } + + updateLastUpdated(elementId) { + const element = document.getElementById(elementId); + if (element) { + element.textContent = new Date().toLocaleTimeString(); + } + } + + initializeEventListeners() { + const taskCommandInput = document.getElementById('taskCommand'); + if (taskCommandInput) { + taskCommandInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + this.executeTaskCommand(); + } + }); + } + + const searchInput = document.getElementById('taskSearchInput'); + if (searchInput) { + searchInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + this.handleSearch(); + } + }); + } + } + + setTaskCommand(command) { + const element = document.getElementById('taskCommand'); + if (element) { + element.value = command; + this.executeTaskCommand(); + } + } + + async executeTaskCommand() { + const element = document.getElementById('taskCommand'); + const command = element ? element.value : ''; + + if (!command) { + this.showToast('Please enter a command', 'warning'); + return; + } + + this.setButtonLoading('taskAiBtn', 'Processing...', 'fa-spinner fa-spin'); + + try { + // Simulate AI processing + await new Promise(resolve => setTimeout(resolve, 1500)); + + const response = this.generateTaskAIResponse(command); + this.showAIModal(response); + + this.updateTaskStats(); + this.updateLastUpdated('lastUpdate'); + + } catch (error) { + this.showToast('Error processing command: ' + error.message, 'error'); + } finally { + this.restoreButton('taskAiBtn', 'Execute', 'fa-robot'); + this.clearInput('taskCommand'); + } + } + + generateTaskAIResponse(command) { + const lowerCommand = command.toLowerCase(); + + if (lowerCommand.includes('overdue') || lowerCommand.includes('urgent')) { + return ` +
+

⚠️ Overdue Tasks Analysis

+
+
🚨 Immediate Action Required
+

Found ${this.stats.overdue} overdue tasks that need immediate attention

+
+
+ + Website Security Audit - 2 days overdue +
+
+ + Client Presentation - Due today +
+
+
+
+
💡 AI Recommendations
+
+

• Prioritize security audit first - highest business risk

+

• Delegate or reschedule lower priority tasks

+

• Block 2-hour focus time for urgent items

+
+
+
+ `; + } + + return ` +
+

🤖 Task AI Response

+
+

Processing your request: "${command}"

+

I can help you with:

+
    +
  • • Smart task prioritization and scheduling
  • +
  • • Deadline tracking and alerts
  • +
  • • Progress analysis and reporting
  • +
  • • Workload balancing and optimization
  • +
  • • Task automation and workflows
  • +
+
+
+ `; + } + + // Real API integration functions + async loadTasks(filters = {}) { + try { + this.showLoading(true); + + const params = new URLSearchParams({ + action: 'get_tasks', + ...filters + }); + + console.log('Loading tasks from:', this.apiUrl); + + const response = await fetch(`${this.apiUrl}?${params}`, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + } + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + + if (data.success) { + this.tasks = data.tasks || []; + this.stats = data.stats || this.stats; + this.updateUI(); + this.renderTasks(); + console.log('✅ Tasks loaded successfully:', this.tasks.length); + } else { + throw new Error(data.message || 'Failed to load tasks'); + } + + } catch (error) { + console.error('❌ Error loading tasks:', error); + this.showError('Failed to load tasks: ' + error.message); + } finally { + this.showLoading(false); + } + } + + // Load sample tasks for demonstration + loadSampleTasks() { + console.log('Loading sample tasks...'); + + this.tasks = [ + { + id: 'task_1', + name: 'Website Security Audit', + description: 'Complete security assessment and vulnerability report', + dueDate: '02-01-2025', + dueDateFormatted: '2 days overdue', + timeEstimate: '3 hrs', + status: 'To Do', + priority: 'High', + priorityColor: 'red', + progress: 0, + type: 'Security', + isOverdue: true, + isDueToday: false + }, + { + id: 'task_2', + name: 'Write Blog Article', + description: 'Article on "Top 5 AI Tools for Creators"', + dueDate: '06-01-2025', + dueDateFormatted: 'Due tomorrow', + timeEstimate: '2 hrs', + status: 'In Progress', + priority: 'Medium', + priorityColor: 'orange', + progress: 65, + type: 'Writing', + isOverdue: false, + isDueToday: false + }, + { + id: 'task_3', + name: 'Design Landing Page', + description: 'Create a responsive landing page using Tailwind', + dueDate: '05-01-2025', + dueDateFormatted: 'Due today', + timeEstimate: '3 hrs', + status: 'To Do', + priority: 'Medium', + priorityColor: 'orange', + progress: 0, + type: 'Design', + isOverdue: false, + isDueToday: true + }, + { + id: 'task_4', + name: 'Deploy Production Update', + description: 'Release v2.1.3 to production servers', + dueDate: '03-01-2025', + dueDateFormatted: 'Completed yesterday', + timeEstimate: '1 hr', + status: 'Done', + priority: 'High', + priorityColor: 'red', + progress: 100, + type: 'Development', + isOverdue: false, + isDueToday: false + } + ]; + + this.stats = { + total: this.tasks.length, + overdue: this.tasks.filter(t => t.isOverdue).length, + dueToday: this.tasks.filter(t => t.isDueToday).length, + completed: this.tasks.filter(t => t.status === 'Done').length, + inProgress: this.tasks.filter(t => t.status === 'In Progress').length, + toDo: this.tasks.filter(t => t.status === 'To Do').length + }; + + this.updateUI(); + this.renderTasks(); + this.showLoading(false); + } + + async searchTasks(query) { + if (!query.trim()) { + this.loadTasks(); + return; + } + + // Filter current tasks for demo + const filtered = this.tasks.filter(task => + task.name.toLowerCase().includes(query.toLowerCase()) || + task.description.toLowerCase().includes(query.toLowerCase()) + ); + + this.renderFilteredTasks(filtered); + this.showToast(`Found ${filtered.length} tasks matching "${query}"`, 'info'); + } + + async filterTasks(status) { + console.log('Filtering by status:', status); + + if (status === 'all') { + this.renderTasks(); + return; + } + + const statusMap = { + 'todo': 'To Do', + 'inprogress': 'In Progress', + 'done': 'Done' + }; + + const targetStatus = statusMap[status] || status; + const filtered = this.tasks.filter(task => task.status === targetStatus); + this.renderFilteredTasks(filtered); + this.showToast(`Showing ${filtered.length} ${targetStatus} tasks`, 'info'); + } + + renderTasks() { + this.renderFilteredTasks(this.tasks); + } + + renderFilteredTasks(tasks) { + const todoColumn = document.querySelector('#todoColumn .kanban-column'); + const inProgressColumn = document.querySelector('#inProgressColumn .kanban-column'); + const doneColumn = document.querySelector('#doneColumn .kanban-column'); + + if (!todoColumn || !inProgressColumn || !doneColumn) { + console.error('Kanban columns not found'); + return; + } + + // Clear existing tasks + todoColumn.innerHTML = ''; + inProgressColumn.innerHTML = ''; + doneColumn.innerHTML = ''; + + // Group tasks by status + const tasksByStatus = { + 'To Do': [], + 'In Progress': [], + 'Done': [] + }; + + tasks.forEach(task => { + if (tasksByStatus[task.status]) { + tasksByStatus[task.status].push(task); + } + }); + + // Render tasks in each column + Object.keys(tasksByStatus).forEach(status => { + const column = status === 'To Do' ? todoColumn : + status === 'In Progress' ? inProgressColumn : doneColumn; + + tasksByStatus[status].forEach(task => { + column.appendChild(this.createTaskCard(task)); + }); + }); + + // Update column counts + this.updateColumnCounts(tasksByStatus); + } + + createTaskCard(task) { + const card = document.createElement('div'); + + let cardClass = 'task-card bg-gray-50 p-4 rounded-lg border border-gray-200 cursor-pointer hover:shadow-md transition-all'; + if (task.isOverdue) { + cardClass = 'task-card task-overdue bg-red-50 p-4 rounded-lg border border-red-200 cursor-pointer hover:shadow-md transition-all'; + } else if (task.isDueToday) { + cardClass = 'task-card task-due-today bg-orange-50 p-4 rounded-lg border border-orange-200 cursor-pointer hover:shadow-md transition-all'; + } else if (task.status === 'Done') { + cardClass = 'task-card task-completed bg-green-50 p-4 rounded-lg border border-green-200 cursor-pointer hover:shadow-md transition-all'; + } + + card.className = cardClass; + card.setAttribute('data-task-id', task.id); + + const priorityBadgeColor = task.priority === 'High' ? 'red' : + task.priority === 'Medium' ? 'orange' : 'green'; + + let progressBar = ''; + if (task.status === 'In Progress') { + progressBar = ` +
+
+
+
+ ${task.progress}% complete + ${task.dueDateFormatted} +
+ `; + } else { + progressBar = ` +
+ ${task.dueDateFormatted} + ${task.type ? `${task.type}` : ''} +
+ `; + } + + card.innerHTML = ` +
+

${task.name}

+ ${task.priority} +
+

${task.description}

+ ${progressBar} + ${task.timeEstimate !== 'Not specified' ? ` +
+ Estimated: ${task.timeEstimate} +
+ ` : ''} + ${task.status === 'Done' ? ` +
+ Completed + Yesterday +
+ ` : ''} + `; + + // Add click handler for task details + card.addEventListener('click', () => this.showTaskDetails(task)); + + return card; + } + + updateColumnCounts(tasksByStatus) { + const todoCountEl = document.querySelector('#todoColumn .text-gray-500'); + const inProgressCountEl = document.querySelector('#inProgressColumn .text-gray-500'); + const doneCountEl = document.querySelector('#doneColumn .text-gray-500'); + + if (todoCountEl) todoCountEl.textContent = tasksByStatus['To Do'].length; + if (inProgressCountEl) inProgressCountEl.textContent = tasksByStatus['In Progress'].length; + if (doneCountEl) doneCountEl.textContent = tasksByStatus['Done'].length; + } + + updateUI() { + // Update stats dashboard + this.updateElement('overdueCount', this.stats.overdue); + this.updateElement('dueTodayCount', this.stats.dueToday); + this.updateElement('totalTasks', this.stats.total); + this.updateElement('completedToday', this.stats.completed); + + this.updateLastUpdated('lastUpdate'); + } + + updateElement(id, value) { + const element = document.getElementById(id); + if (element) { + element.textContent = value; + } + } + + showTaskDetails(task) { + const modalContent = ` +
+
+

${task.name}

+ ${task.priority} Priority +
+ +
+
Description
+

${task.description}

+
+ +
+
+
Status
+

${task.status}

+
+
+
Due Date
+

${task.dueDateFormatted}

+
+
+ + ${task.status === 'In Progress' ? ` +
+
Progress
+
+
+
+

${task.progress}% Complete

+
+ ` : ''} + +
+
+
Type
+

${task.type}

+
+
+
Time Estimate
+

${task.timeEstimate}

+
+
+ +
+ + +
+
+ `; + + this.showAIModal(modalContent); + } + + showLoading(show) { + const loadingElement = document.getElementById('loadingState'); + const kanbanColumns = document.querySelectorAll('.kanban-column'); + + if (loadingElement) { + loadingElement.classList.toggle('hidden', !show); + } + + if (show) { + kanbanColumns.forEach(column => { + column.innerHTML = '
Loading...
'; + }); + } + } + + showError(message) { + const todoColumn = document.querySelector('#todoColumn .kanban-column'); + if (todoColumn) { + todoColumn.innerHTML = ` +
+
+ +
+

Error Loading Tasks

+

${message}

+ +
+ `; + } + } + + updateTaskStats() { + this.totalActions++; + } + + // Utility functions + setButtonLoading(elementId, text, iconClass) { + const element = document.getElementById(elementId); + if (element) { + element.innerHTML = ` ${text}`; + element.disabled = true; + } + } + + restoreButton(elementId, text, iconClass) { + const element = document.getElementById(elementId); + if (element) { + element.innerHTML = ` ${text}`; + element.disabled = false; + } + } + + clearInput(elementId) { + const element = document.getElementById(elementId); + if (element) { + element.value = ''; + } + } + + showAIModal(content) { + const modal = document.getElementById('aiModal'); + const responseDiv = document.getElementById('aiResponse'); + if (modal && responseDiv) { + responseDiv.innerHTML = content; + modal.classList.remove('hidden'); + } + } + + hideAIModal() { + const modal = document.getElementById('aiModal'); + if (modal) { + modal.classList.add('hidden'); + } + } + + showToast(message, type = 'info') { + const toast = document.createElement('div'); + toast.className = `fixed top-4 right-4 z-50 max-w-sm w-full transform transition-all duration-300`; + + const bgColor = { + success: 'bg-green-500', + error: 'bg-red-500', + warning: 'bg-yellow-500', + info: 'bg-blue-500' + }[type] || 'bg-gray-500'; + + const icon = { + success: 'fas fa-check-circle', + error: 'fas fa-exclamation-circle', + warning: 'fas fa-exclamation-triangle', + info: 'fas fa-info-circle' + }[type] || 'fas fa-info-circle'; + + toast.innerHTML = ` +
+ + ${message} + +
+ `; + + document.body.appendChild(toast); + + // Animate in + setTimeout(() => { + toast.style.transform = 'translateX(0)'; + }, 100); + + // Auto remove after 4 seconds + setTimeout(() => { + toast.style.transform = 'translateX(100%)'; + setTimeout(() => { + if (toast.parentElement) { + toast.remove(); + } + }, 300); + }, 4000); + } + + // Quick action functions + createTask() { + this.showAIModal(` +
+

✅ Create New Task

+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
+ `); + } + + async submitNewTask() { + const name = document.getElementById('newTaskName')?.value || ''; + const description = document.getElementById('newTaskDescription')?.value || ''; + const dueDate = document.getElementById('newTaskDue')?.value || ''; + const priority = document.getElementById('newTaskPriority')?.value || 'Medium'; + const timeEstimate = document.getElementById('newTaskTime')?.value || '1 hr'; + + if (!name.trim()) { + this.showToast('Please enter a task name', 'warning'); + return; + } + + try { + // In a real implementation, you would send this to your n8n workflow + // For now, we'll simulate success and add to local data + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Add new task to local data for demo + const newTask = { + id: 'task_' + Date.now(), + name: name, + description: description || 'No description provided', + dueDate: dueDate, + dueDateFormatted: dueDate ? 'Due ' + new Date(dueDate).toLocaleDateString() : 'No due date', + timeEstimate: timeEstimate, + status: 'To Do', + priority: priority, + priorityColor: priority === 'High' ? 'red' : priority === 'Medium' ? 'orange' : 'green', + progress: 0, + type: 'General', + isOverdue: false, + isDueToday: false + }; + + this.tasks.unshift(newTask); + this.stats.total++; + this.stats.toDo++; + + this.hideAIModal(); + this.showToast(`Task "${name}" created successfully!`, 'success'); + this.updateUI(); + this.renderTasks(); + + } catch (error) { + this.showToast('Failed to create task: ' + error.message, 'error'); + } + } + + async updateTaskStatus(taskId, newStatus) { + try { + const task = this.tasks.find(t => t.id === taskId); + if (task) { + const oldStatus = task.status; + task.status = newStatus; + + if (newStatus === 'Done') { + task.progress = 100; + } else if (newStatus === 'In Progress' && task.progress === 0) { + task.progress = 25; + } + + // Update stats + if (oldStatus === 'To Do') this.stats.toDo--; + else if (oldStatus === 'In Progress') this.stats.inProgress--; + else if (oldStatus === 'Done') this.stats.completed--; + + if (newStatus === 'To Do') this.stats.toDo++; + else if (newStatus === 'In Progress') this.stats.inProgress++; + else if (newStatus === 'Done') this.stats.completed++; + + this.hideAIModal(); + this.showToast(`Task status updated to ${newStatus}`, 'success'); + this.updateUI(); + this.renderTasks(); + } + } catch (error) { + this.showToast('Failed to update task: ' + error.message, 'error'); + } + } + + prioritizeTasks() { + this.updateTaskStats(); + this.showToast('🎯 Tasks automatically prioritized by AI algorithm!', 'success'); + + // Sort tasks by priority and overdue status + this.tasks.sort((a, b) => { + if (a.isOverdue && !b.isOverdue) return -1; + if (!a.isOverdue && b.isOverdue) return 1; + + const priorityOrder = { 'High': 0, 'Medium': 1, 'Low': 2 }; + return (priorityOrder[a.priority] || 2) - (priorityOrder[b.priority] || 2); + }); + + this.renderTasks(); + } + + bulkUpdate() { + this.updateTaskStats(); + this.showToast('📝 Bulk update feature coming soon!', 'info'); + } + + generateReport() { + const completionRate = this.stats.total > 0 ? Math.round((this.stats.completed / this.stats.total) * 100) : 0; + + this.showAIModal(` +
+

📊 Progress Report

+ +
+
+
${this.stats.total}
+
Total Tasks
+
+
+
${completionRate}%
+
Completion Rate
+
+
+ +
+
Task Breakdown
+
+
+ Completed: + ${this.stats.completed} +
+
+ In Progress: + ${this.stats.inProgress} +
+
+ To Do: + ${this.stats.toDo} +
+
+ Overdue: + ${this.stats.overdue} +
+
+
+ +
+
💡 AI Insights
+
    +
  • • Your completion rate is ${completionRate > 70 ? 'excellent' : completionRate > 50 ? 'good' : 'needs improvement'}
  • +
  • • ${this.stats.overdue > 0 ? `Focus on ${this.stats.overdue} overdue tasks first` : 'Great job staying on top of deadlines!'}
  • +
  • • Consider breaking down large tasks into smaller chunks
  • +
+
+
+ `); + } + + // Search and filter functions + async handleSearch() { + const searchInput = document.getElementById('taskSearchInput'); + if (searchInput) { + await this.searchTasks(searchInput.value); + } + } + + async handleFilter(status) { + await this.filterTasks(status); + + // Update filter button states + document.querySelectorAll('.filter-btn').forEach(btn => { + btn.classList.remove('bg-orange-200', 'text-orange-800'); + btn.classList.add('bg-gray-100', 'text-gray-700'); + }); + + const activeBtn = document.querySelector(`[data-status="${status}"]`); + if (activeBtn) { + activeBtn.classList.remove('bg-gray-100', 'text-gray-700'); + activeBtn.classList.add('bg-orange-200', 'text-orange-800'); + } + } +} + +// Initialize Task Agent when DOM is loaded +document.addEventListener('DOMContentLoaded', function() { + console.log('Initializing Task Agent...'); + window.taskAgent = new TaskAgent(); + + // Make functions globally available for onclick handlers + window.executeTaskCommand = () => taskAgent.executeTaskCommand(); + window.setTaskCommand = (cmd) => taskAgent.setTaskCommand(cmd); + window.createTask = () => taskAgent.createTask(); + window.prioritizeTasks = () => taskAgent.prioritizeTasks(); + window.bulkUpdate = () => taskAgent.bulkUpdate(); + window.generateReport = () => taskAgent.generateReport(); + window.closeAIModal = () => taskAgent.hideAIModal(); + + console.log('✅ Task Agent initialized successfully'); +}); + +// Global helper functions +function goHome() { + window.location.href = 'index.html'; +} + +console.log('✅ Task Agent JS loaded successfully'); \ No newline at end of file diff --git a/assets/js/trade-intelligence.js b/assets/js/trade-intelligence.js new file mode 100644 index 0000000..e81d55a --- /dev/null +++ b/assets/js/trade-intelligence.js @@ -0,0 +1,1066 @@ +/** + * Enhanced Trade Intelligence Functions + * With searchable country dropdown and improved offline handling + */ + +// Global variables +let currentSearchData = []; +let buyersData = []; +let sellersData = []; + +// Enhanced country list +const availableCountries = [ + 'Afghanistan', 'Albania', 'Algeria', 'Argentina', 'Armenia', 'Australia', 'Austria', 'Azerbaijan', + 'Bahrain', 'Bangladesh', 'Belarus', 'Belgium', 'Bolivia', 'Brazil', 'Bulgaria', + 'Cambodia', 'Canada', 'Chile', 'China', 'Colombia', 'Croatia', 'Czech Republic', + 'Denmark', 'Ecuador', 'Egypt', 'Estonia', 'Ethiopia', 'Finland', 'France', + 'Georgia', 'Germany', 'Ghana', 'Greece', 'Hungary', 'Iceland', 'India', 'Indonesia', + 'Iran', 'Iraq', 'Ireland', 'Israel', 'Italy', 'Japan', 'Jordan', 'Kazakhstan', + 'Kenya', 'Kuwait', 'Latvia', 'Lebanon', 'Lithuania', 'Luxembourg', 'Malaysia', + 'Mexico', 'Morocco', 'Netherlands', 'New Zealand', 'Nigeria', 'Norway', 'Pakistan', + 'Peru', 'Philippines', 'Poland', 'Portugal', 'Qatar', 'Romania', 'Russia', + 'Saudi Arabia', 'Singapore', 'Slovakia', 'Slovenia', 'South Africa', 'South Korea', + 'Spain', 'Sri Lanka', 'Sweden', 'Switzerland', 'Taiwan', 'Thailand', 'Turkey', + 'UAE', 'Ukraine', 'United Kingdom', 'United States', 'Uruguay', 'Vietnam' +]; + +// Function to create searchable country dropdown +function createSearchableCountryDropdown(selectElement, withAllCountries = true) { + if (!selectElement) { + console.warn('Select element not found'); + return; + } + + const container = selectElement.parentNode; + const wrapper = document.createElement('div'); + wrapper.className = 'relative w-full'; + + // Create input field that looks like the original select + const input = document.createElement('input'); + input.type = 'text'; + input.placeholder = 'Type to search countries...'; + input.className = selectElement.className + ' pr-8'; + input.id = selectElement.id + '_input'; + + // Add search icon + const searchIcon = document.createElement('div'); + searchIcon.className = 'absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 pointer-events-none'; + searchIcon.innerHTML = ''; + + // Create dropdown container + const dropdown = document.createElement('div'); + dropdown.className = 'absolute z-20 w-full bg-white border border-gray-300 rounded-lg shadow-lg max-h-48 overflow-y-auto hidden mt-1'; + dropdown.style.top = '100%'; + + // Prepare countries list + const countries = withAllCountries ? ['All Countries', ...availableCountries] : availableCountries; + + // Function to create dropdown options + const createOptions = (filteredCountries) => { + dropdown.innerHTML = ''; + + if (filteredCountries.length === 0) { + dropdown.innerHTML = '
No countries found
'; + return; + } + + filteredCountries.forEach(country => { + const option = document.createElement('div'); + option.className = 'p-3 hover:bg-blue-50 cursor-pointer text-sm border-b border-gray-100 last:border-b-0 transition-colors'; + option.textContent = country; + + // Add click handler + option.addEventListener('click', () => { + input.value = country; + selectElement.value = country; + dropdown.classList.add('hidden'); + + // Update search icon to checkmark temporarily + searchIcon.innerHTML = ''; + setTimeout(() => { + searchIcon.innerHTML = ''; + }, 1000); + + // Trigger change event on original select + const changeEvent = new Event('change', { bubbles: true }); + selectElement.dispatchEvent(changeEvent); + + console.log('Selected country:', country); + }); + + dropdown.appendChild(option); + }); + }; + + // Initialize with all countries + createOptions(countries); + + // Handle input changes (search functionality) + input.addEventListener('input', (e) => { + const query = e.target.value.toLowerCase().trim(); + + if (query === '') { + createOptions(countries); + } else { + const filtered = countries.filter(country => + country.toLowerCase().includes(query) + ); + createOptions(filtered); + } + + dropdown.classList.remove('hidden'); + }); + + // Show dropdown on focus + input.addEventListener('focus', () => { + dropdown.classList.remove('hidden'); + }); + + // Handle keyboard navigation + input.addEventListener('keydown', (e) => { + const options = dropdown.querySelectorAll('div[class*="cursor-pointer"]'); + let currentIndex = -1; + + // Find currently highlighted option + options.forEach((option, index) => { + if (option.classList.contains('bg-blue-100')) { + currentIndex = index; + } + }); + + if (e.key === 'ArrowDown') { + e.preventDefault(); + // Remove current highlight + if (currentIndex >= 0) { + options[currentIndex].classList.remove('bg-blue-100'); + } + // Add highlight to next option + currentIndex = (currentIndex + 1) % options.length; + if (options[currentIndex]) { + options[currentIndex].classList.add('bg-blue-100'); + options[currentIndex].scrollIntoView({ block: 'nearest' }); + } + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + // Remove current highlight + if (currentIndex >= 0) { + options[currentIndex].classList.remove('bg-blue-100'); + } + // Add highlight to previous option + currentIndex = currentIndex <= 0 ? options.length - 1 : currentIndex - 1; + if (options[currentIndex]) { + options[currentIndex].classList.add('bg-blue-100'); + options[currentIndex].scrollIntoView({ block: 'nearest' }); + } + } else if (e.key === 'Enter') { + e.preventDefault(); + if (currentIndex >= 0 && options[currentIndex]) { + options[currentIndex].click(); + } + } else if (e.key === 'Escape') { + dropdown.classList.add('hidden'); + input.blur(); + } + }); + + // Hide dropdown when clicking outside + document.addEventListener('click', (e) => { + if (!wrapper.contains(e.target)) { + dropdown.classList.add('hidden'); + } + }); + + // Build the wrapper + wrapper.appendChild(input); + wrapper.appendChild(searchIcon); + wrapper.appendChild(dropdown); + + // Replace original select with wrapper + container.insertBefore(wrapper, selectElement); + + // Hide original select but keep it for form submission + selectElement.style.display = 'none'; + wrapper.appendChild(selectElement); + + console.log('✅ Searchable country dropdown created for:', selectElement.id); + return wrapper; +} + +// Function to initialize all country dropdowns +function initializeSearchableCountryDropdowns() { + console.log('🔄 Initializing searchable country dropdowns...'); + + // Main country filter in advanced search + const countryFilter = document.getElementById('countryFilter'); + if (countryFilter) { + createSearchableCountryDropdown(countryFilter, true); // with "All Countries" option + } + + // CRUD create country dropdown + const createCountry = document.getElementById('createCountry'); + if (createCountry) { + createSearchableCountryDropdown(createCountry, false); // without "All Countries" option + } + + console.log('✅ Searchable country dropdowns initialized'); +} + +// Function to get selected country value (works with both dropdowns) +function getSelectedCountry(selectId) { + const input = document.getElementById(selectId + '_input'); + const select = document.getElementById(selectId); + + if (input && input.value) { + return input.value; + } else if (select && select.value) { + return select.value; + } + + return ''; +} + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', function() { + // Add enter key support + const queryInput = document.getElementById('query'); + if (queryInput) { + queryInput.addEventListener('keypress', function(e) { + if (e.key === 'Enter') { + callWorkflow(); + } + }); + } + + // Initialize searchable country dropdowns with a small delay + setTimeout(() => { + initializeSearchableCountryDropdowns(); + }, 500); + + // Update last updated time + updateLastUpdated(); +}); + +function updateLastUpdated() { + const element = document.getElementById('lastUpdated'); + if (element) { + element.textContent = new Date().toLocaleTimeString(); + } +} + +function setQuickSearch(query) { + console.log('Setting quick search:', query); + document.getElementById('query').value = query; + callWorkflow(); +} + +function quickSearch(type) { + const queries = { + 'all buyers': 'find all buyers', + 'all sellers': 'find all sellers', + 'gulfood data': 'find gulfood data' + }; + + if (queries[type]) { + document.getElementById('query').value = queries[type]; + callWorkflow(); + } +} + +// Updated performAdvancedSearch function +function performAdvancedSearch() { + const searchType = document.getElementById('searchType').value; + const product = document.getElementById('productSearch').value.trim(); + const country = getSelectedCountry('countryFilter'); // Use helper function + + let query = ''; + + // Build query based on selections + if (searchType === 'buyer') { + query = 'find buyers'; + } else if (searchType === 'seller') { + query = 'find sellers'; + } else { + query = 'find companies'; + } + + if (product) { + query += ` with ${product}`; + } + + if (country && country !== 'All Countries') { + query += ` from ${country}`; + } + + document.getElementById('query').value = query; + callWorkflow(); +} + +// Updated resetAdvancedFilters function +function resetAdvancedFilters() { + document.getElementById('searchType').value = 'all'; + document.getElementById('productSearch').value = ''; + + // Reset searchable country dropdown + const countryInput = document.getElementById('countryFilter_input'); + if (countryInput) { + countryInput.value = ''; + } + document.getElementById('countryFilter').value = ''; + + console.log('Advanced filters reset'); +} + +// Enhanced main search function with offline handling +async function callWorkflow() { + const query = document.getElementById('query').value.trim(); + if (!query) { + Utils.toast.warning('Please enter a search query'); + return; + } + + // Check network status before making request + if (Utils.network && !Utils.network.canMakeRequests()) { + return; // User already notified by canMakeRequests() + } + + const searchBtn = document.getElementById('searchBtn'); + const results = document.getElementById('results'); + const resultsSection = document.getElementById('resultsSection'); + const resultsCount = document.getElementById('resultsCount'); + + // Show loading state + searchBtn.innerHTML = ' Searching...'; + searchBtn.disabled = true; + resultsSection.classList.add('hidden'); + + // Show loading skeleton + results.innerHTML = Array(6).fill().map(() => ` +
+
+
+
+
+
+
+
+
+
+
+
+
+ `).join(''); + resultsSection.classList.remove('hidden'); + + try { + console.log('Making API request with query:', query); + + // Use the enhanced API utility + const data = await Utils.api.get( + 'https://thecyberlearn.app.n8n.cloud/webhook/search-airtable', + { query: query } + ); + + console.log('Raw API Response:', data); + + // Process the API response + let cards = ''; + let resultCount = 0; + let messageContent = extractMessageContent(data); + + console.log('Message content to parse:', messageContent); + + if (messageContent) { + const companies = parseApiResponse(messageContent); + resultCount = companies.length; + currentSearchData = companies; + + console.log('Parsed companies:', companies); + + companies.forEach((company, index) => { + cards += generateCompanyCard(company, index + 1); + }); + } + + // Update UI + resultsCount.textContent = `Found ${resultCount} companies matching "${query}"`; + updateLastUpdated(); + + if (cards && resultCount > 0) { + results.innerHTML = cards; + resultsSection.classList.remove('hidden'); + console.log('✅ Showing results:', resultCount); + } else { + // Show no results message (not an error) + results.innerHTML = ` +
+
🔍
+

No results found

+

Try a different search term or check the query format

+
+

Try these formats:

+
+

• "find all buyers"

+

• "find sellers from india"

+

• "search buyers uk"

+

• "find pepsi sellers"

+
+
+
`; + resultsSection.classList.remove('hidden'); + console.log('❌ No results found'); + } + + } catch (error) { + console.error('Search error:', error); + + // Show offline-friendly error message with retry button + results.innerHTML = ` +
+
📱
+

Unable to search right now

+

Please check your internet connection and try again

+
+ +
+
`; + resultsSection.classList.remove('hidden'); + } finally { + searchBtn.innerHTML = ' Search'; + searchBtn.disabled = false; + } +} + +// Updated performProductMatching function +async function performProductMatching() { + const product = document.getElementById('productSearch').value.trim(); + const country = getSelectedCountry('countryFilter'); // Use helper function + + if (!product) { + Utils.toast.warning('Please enter a product in the Product/Brand field to find matches'); + return; + } + + console.log('Starting product matching for:', product, 'in country:', country); + + // Show loading in the inline results + const resultsSection = document.getElementById('productMatchingResults'); + const productHeader = document.getElementById('productInfoHeader'); + + resultsSection.classList.remove('hidden'); + productHeader.innerHTML = ` +
+
+

Finding buyers and sellers for ${product}${country && country !== 'All Countries' ? ` in ${country}` : ''}...

+
+ `; + + try { + // Search for buyers + const buyerQuery = `find buyers with ${product}${country && country !== 'All Countries' ? ` from ${country}` : ''}`; + console.log('Buyer query:', buyerQuery); + + const buyerResponse = await Utils.api.get( + 'https://thecyberlearn.app.n8n.cloud/webhook/search-airtable', + { query: buyerQuery } + ); + + // Search for sellers + const sellerQuery = `find sellers with ${product}${country && country !== 'All Countries' ? ` from ${country}` : ''}`; + console.log('Seller query:', sellerQuery); + + const sellerResponse = await Utils.api.get( + 'https://thecyberlearn.app.n8n.cloud/webhook/search-airtable', + { query: sellerQuery } + ); + + console.log('Buyer response:', buyerResponse); + console.log('Seller response:', sellerResponse); + + // Parse responses + const buyers = parseApiResponse(extractMessageContent(buyerResponse)); + const sellers = parseApiResponse(extractMessageContent(sellerResponse)); + + console.log('Parsed buyers:', buyers.length); + console.log('Parsed sellers:', sellers.length); + + displayProductMatchResults(product, buyers, sellers, country); + + } catch (error) { + console.error('Product matching error:', error); + productHeader.innerHTML = ` +
+
⚠️
+

Search failed

+

${error.message}

+ +
+ `; + } +} + +// Show product matching results inline instead of modal +// FIXED displayProductMatchResults function - Replace in your trade-intelligence.js + +function displayProductMatchResults(product, buyers, sellers, country) { + const resultsSection = document.getElementById('productMatchingResults'); + const productHeader = document.getElementById('productInfoHeader'); + const buyersTitle = document.getElementById('buyersTitle'); + const sellersTitle = document.getElementById('sellersTitle'); + const buyersList = document.getElementById('buyersList'); + const sellersList = document.getElementById('sellersList'); + + // Check if elements exist before trying to update them + if (!resultsSection || !productHeader || !buyersTitle || !sellersTitle || !buyersList || !sellersList) { + console.error('Required elements not found for product matching results'); + return; + } + + // Update header info + productHeader.innerHTML = ` +
+
+

Product: ${product}

+ ${country && country !== 'All Countries' ? `

Filtered by: ${country}

` : ''} +
+
+ ${buyers.length} Buyers + ${sellers.length} Sellers +
+
+ `; + + // Update titles + buyersTitle.textContent = `Buyers (${buyers.length})`; + sellersTitle.textContent = `Sellers (${sellers.length})`; + + // Populate buyers list + if (buyers.length > 0) { + buyersList.innerHTML = buyers.map(buyer => ` +
+
${buyer.name}
+

${buyer.address}

+ ${buyer.email ? `

${buyer.email}

` : ''} + ${buyer.phone ? `

${buyer.phone}

` : ''} +
+ `).join(''); + } else { + buyersList.innerHTML = '

No buyers found for this product

'; + } + + // Populate sellers list + if (sellers.length > 0) { + sellersList.innerHTML = sellers.map(seller => ` +
+
${seller.name}
+

${seller.address}

+ ${seller.email ? `

${seller.email}

` : ''} + ${seller.phone ? `

${seller.phone}

` : ''} +
+ `).join(''); + } else { + sellersList.innerHTML = '

No sellers found for this product

'; + } + + // Show the results section + resultsSection.classList.remove('hidden'); + + // Scroll to results + resultsSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); + + console.log('✅ Product matching results displayed successfully'); +} + +// Close product matching results +function closeProductMatching() { + document.getElementById('productMatchingResults').classList.add('hidden'); +} + +// Helper function to extract message content +function extractMessageContent(data) { + if (data && Array.isArray(data) && data.length > 0) { + const apiData = data[0]; + if (apiData.success && apiData.content) { + return apiData.content.originalMessage || apiData.content.message; + } else if (apiData.message) { + return apiData.message; + } + } else if (data && data.success && data.content) { + return data.content.originalMessage || data.content.message; + } else if (data && data.message) { + return data.message; + } else if (typeof data === 'string') { + return data; + } + return ''; +} + +// Generate country breakdown +function generateCountryBreakdown(companies) { + const countryCount = {}; + companies.forEach(company => { + const country = company.address || 'Unknown'; + countryCount[country] = (countryCount[country] || 0) + 1; + }); + + const sortedCountries = Object.entries(countryCount) + .sort(([,a], [,b]) => b - a) + .slice(0, 5); + + return sortedCountries.map(([country, count]) => + `
+ ${country} + ${count} +
` + ).join(''); +} + +// Sort results function +function sortResults() { + const sortBy = document.getElementById('sortBy').value; + if (!currentSearchData.length) return; + + let sortedData = [...currentSearchData]; + + switch (sortBy) { + case 'name': + sortedData.sort((a, b) => (a.name || '').localeCompare(b.name || '')); + break; + case 'country': + sortedData.sort((a, b) => (a.address || '').localeCompare(b.address || '')); + break; + case 'products': + sortedData.sort((a, b) => { + const aProducts = (a.products || []).join(' '); + const bProducts = (b.products || []).join(' '); + return aProducts.localeCompare(bProducts); + }); + break; + default: + break; + } + + const results = document.getElementById('results'); + let cards = ''; + sortedData.forEach((company, index) => { + cards += generateCompanyCard(company, index + 1); + }); + results.innerHTML = cards; +} + +// Parse API response function (keeping your existing implementation) +function parseApiResponse(responseText) { + let companies = []; + + try { + console.log('Raw response text length:', responseText.length); + console.log('First 200 chars:', responseText.substring(0, 200)); + + if (responseText.includes('No results found') || + responseText.includes('Found 0 companies') || + responseText.includes('Could not find')) { + console.log('API indicates no results found'); + return companies; + } + + let cleanText = responseText + .replace(/<[^>]*>/g, '') + .replace(/\*\*/g, '') + .replace(/🔍.*?🚢/g, '') + .replace(/🔍.*?📦/g, '') + .replace(/📊.*?────+/g, '') + .replace(/💡.*$/gm, '') + .replace(/Complete dataset returned.*$/gm, '') + .trim(); + + const splitPattern = /\b(\d+)\.\s+/; + const parts = cleanText.split(splitPattern); + + const companyBlocks = []; + for (let i = 2; i < parts.length; i += 2) { + if (parts[i] && parts[i].trim()) { + companyBlocks.push(parts[i].trim()); + } + } + + companyBlocks.forEach((block, index) => { + if (!block || block.trim().length === 0) return; + + const lines = block.split('\n').map(line => line.trim()).filter(line => line.length > 0); + const companyName = lines[0] || ''; + + const phoneMatches = block.match(/☎️\s*([\d\s\-\(\)\.E\+]+)/g) || + block.match(/(?:phone|tel|contact)[:]\s*([\d\s\-\(\)\.E\+]+)/gi) || + block.match(/(\+\d{1,3}[\s\-]?\d{1,4}[\s\-]?\d{1,4}[\s\-]?\d{1,9})/g) || []; + const phone = phoneMatches[0] ? phoneMatches[0].replace(/☎️\s*/, '').replace(/(?:phone|tel|contact)[:]\s*/gi, '').trim() : ''; + + const emailMatches = block.match(/📧\s*([^\s\n]+@[^\s\n]+)/g) || + block.match(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g) || []; + const email = emailMatches[0] ? emailMatches[0].replace(/📧\s*/, '').trim() : ''; + + const websiteMatches = block.match(/🔗\s*(https?:\/\/[^\s\n]+)/g) || + block.match(/(https?:\/\/[^\s\n]+)/g) || + block.match(/(?:website|www)[:]\s*([^\s\n]+)/gi) || []; + const website = websiteMatches[0] ? websiteMatches[0].replace(/🔗\s*/, '').replace(/(?:website|www)[:]\s*/gi, '').replace(/\/$/, '') : ''; + + const locationMatches = block.match(/📍\s*([^\n🔗📧☎️📦🏷️]+)/g) || + block.match(/(?:location|address|country)[:]\s*([^\n]+)/gi) || []; + let location = 'Unknown'; + + if (locationMatches.length > 0) { + location = locationMatches[0].replace(/📍\s*/, '').replace(/(?:location|address|country)[:]\s*/gi, '').trim(); + } + + if (location === 'Unknown' || location.length < 2) { + if (block.includes('United Kingdom') || block.includes('UK')) 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'; + else if (block.includes('Germany')) location = 'Germany'; + else if (block.includes('France')) location = 'France'; + else if (block.includes('Italy')) location = 'Italy'; + else if (block.includes('Spain')) location = 'Spain'; + else if (block.includes('Canada')) location = 'Canada'; + else if (block.includes('Australia')) location = 'Australia'; + else if (block.includes('Japan')) location = 'Japan'; + } + + const productMatches = block.match(/📦\s*([^\n🏷️]+)/g) || + block.match(/(?:products|items|goods)[:]\s*([^\n]+)/gi) || []; + const brandMatches = block.match(/🏷️\s*([^\n]+)/g) || + block.match(/(?:brands|brand)[:]\s*([^\n]+)/gi) || []; + + const products = []; + + productMatches.forEach(match => { + const productText = match.replace(/📦\s*/, '').replace(/(?:products|items|goods)[:]\s*/gi, '').trim(); + const productList = productText.split(/[,&]/).map(p => p.trim()).filter(p => p.length > 0); + products.push(...productList); + }); + + brandMatches.forEach(match => { + const brandText = match.replace(/🏷️\s*/, '').replace(/(?:brands|brand)[:]\s*/gi, '').trim(); + const brandList = brandText.split(/[,&]/).map(b => b.trim()).filter(b => b.length > 0); + products.push(...brandList); + }); + + if (companyName && companyName.length > 0 && !companyName.toLowerCase().includes('no result')) { + const company = { + name: companyName, + phone: phone, + email: email, + website: website, + address: location, + products: products.slice(0, 8) + }; + + companies.push(company); + console.log(`✅ Added company:`, company); + } + }); + + console.log(`Final parsed companies count: ${companies.length}`); + + } catch (error) { + console.error('Error parsing API response:', error); + } + + return companies; +} + +// Generate company card function +function generateCompanyCard(company, index) { + const location = company.address?.toLowerCase() || ''; + let flagHtml = ''; + + if (location.includes('india')) { + flagHtml = '
'; + } else if (location.includes('united kingdom') || location.includes('uk')) { + flagHtml = '
'; + } else if (location.includes('usa') || location.includes('united states')) { + flagHtml = '
'; + } else if (location.includes('uae') || location.includes('dubai')) { + flagHtml = '
'; + } else if (location.includes('china')) { + flagHtml = '
'; + } else { + flagHtml = '
🌍
'; + } + + return ` +
+
+ ${flagHtml} +

${company.name}

+
+ +
+ ${company.phone ? ` +
+ + ${company.phone} +
+ ` : ''} + + ${company.email ? ` + + ` : ''} + + ${company.website ? ` + + ` : ''} + + ${company.address ? ` +
+ + ${company.address} +
+ ` : ''} +
+ + ${company.products && company.products.length > 0 ? ` +
+ ${company.products.slice(0, 3).map(product => ` + ${product} + `).join('')} + ${company.products.length > 3 ? ` + +${company.products.length - 3} more + ` : ''} +
+ ` : ''} +
+ `; +} + +// Export results function +function exportResults() { + if (currentSearchData.length === 0) { + Utils.toast.warning('No data to export'); + 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); + + Utils.toast.success('Export completed successfully!'); +} + +// Clear results function +function clearResults() { + document.getElementById('resultsSection').classList.add('hidden'); + currentSearchData = []; +} + +// CRUD Modal functions +function toggleCrudModal() { + const modal = document.getElementById('crudModal'); + if (modal.classList.contains('hidden')) { + modal.classList.remove('hidden'); + selectCrudOperation('create'); + } else { + closeCrudModal(); + } +} + +function closeCrudModal() { + document.getElementById('crudModal').classList.add('hidden'); + resetAllCrudForms(); +} + +function selectCrudOperation(operation) { + document.getElementById('createOperation').classList.add('hidden'); + document.getElementById('updateOperation').classList.add('hidden'); + document.getElementById('deleteOperation').classList.add('hidden'); + + const tabs = ['createTab', 'updateTab', 'deleteTab']; + tabs.forEach(tab => { + const el = document.getElementById(tab); + el.className = 'flex-1 py-3 px-4 rounded-md font-medium transition-all flex items-center justify-center gap-2 text-gray-600 hover:text-gray-800'; + }); + + document.getElementById(operation + 'Operation').classList.remove('hidden'); + + const activeTab = document.getElementById(operation + 'Tab'); + if (operation === 'create') { + activeTab.className = 'flex-1 py-3 px-4 rounded-md font-medium transition-all flex items-center justify-center gap-2 bg-green-600 text-white'; + } else if (operation === 'update') { + activeTab.className = 'flex-1 py-3 px-4 rounded-md font-medium transition-all flex items-center justify-center gap-2 bg-blue-600 text-white'; + } else if (operation === 'delete') { + activeTab.className = 'flex-1 py-3 px-4 rounded-md font-medium transition-all flex items-center justify-center gap-2 bg-red-600 text-white'; + setupDeleteValidation(); + } +} + +function setupDeleteValidation() { + const checkbox = document.getElementById('deleteConfirm'); + const deleteBtn = document.getElementById('deleteBtn'); + + checkbox.addEventListener('change', function() { + if (this.checked) { + deleteBtn.disabled = false; + deleteBtn.classList.remove('opacity-50', 'cursor-not-allowed'); + } else { + deleteBtn.disabled = true; + deleteBtn.classList.add('opacity-50', 'cursor-not-allowed'); + } + }); +} + +async function executeCrud(operation) { + const button = document.getElementById(operation + 'Btn'); + const originalText = button.innerHTML; + + let command = ''; + if (operation === 'create') { + command = buildCreateCommand(); + } else if (operation === 'update') { + command = buildUpdateCommand(); + } else if (operation === 'delete') { + command = buildDeleteCommand(); + } + + if (!command) { + Utils.toast.warning('Please fill in all required fields'); + return; + } + + button.innerHTML = 'Processing...'; + button.disabled = true; + + try { + console.log('Executing CRUD command:', command); + + const result = await Utils.api.get( + 'https://thecyberlearn.app.n8n.cloud/webhook/search-airtable', + { query: command } + ); + + const message = extractMessageContent(result) || 'Operation completed successfully'; + const cleanMessage = message.replace(/<[^>]*>/g, '').replace(/\*\*/g, '').trim(); + + Utils.toast.success(cleanMessage); + resetAllCrudForms(); + closeCrudModal(); + + if (currentSearchData.length > 0) { + setTimeout(() => { + const currentQuery = document.getElementById('query').value; + if (currentQuery) callWorkflow(); + }, 1000); + } + + } catch (error) { + console.error('CRUD operation error:', error); + Utils.toast.error('Operation failed. Please try again.'); + } finally { + button.innerHTML = originalText; + button.disabled = false; + } +} + +function buildCreateCommand() { + const type = document.getElementById('createType').value; + const name = document.getElementById('createName').value.trim(); + const country = getSelectedCountry('createCountry'); + const email = document.getElementById('createEmail').value.trim(); + const phone = document.getElementById('createPhone').value.trim(); + const website = document.getElementById('createWebsite').value.trim(); + const products = document.getElementById('createProducts').value.trim(); + const address = document.getElementById('createAddress').value.trim(); + + if (!type || !name) return ''; + + let command = `create ${type} ${name}`; + if (country) command += ` from ${country}`; + if (email) command += ` email ${email}`; + if (phone) command += ` phone ${phone}`; + if (website) command += ` website ${website}`; + if (products) command += ` products ${products}`; + if (address) command += ` address ${address}`; + + return command; +} + +function buildUpdateCommand() { + const name = document.getElementById('updateName').value.trim(); + const field = document.getElementById('updateField').value; + const value = document.getElementById('updateValue').value.trim(); + + if (!name || !field || !value) return ''; + + return `update ${name} ${field} to ${value}`; +} + +function buildDeleteCommand() { + const name = document.getElementById('deleteName').value.trim(); + const confirmed = document.getElementById('deleteConfirm').checked; + + if (!name || !confirmed) return ''; + + return `delete ${name}`; +} + +function resetAllCrudForms() { + document.getElementById('createType').value = ''; + document.getElementById('createName').value = ''; + + const createCountryInput = document.getElementById('createCountry_input'); + if (createCountryInput) { + createCountryInput.value = ''; + } + document.getElementById('createCountry').value = ''; + + document.getElementById('createEmail').value = ''; + document.getElementById('createPhone').value = ''; + document.getElementById('createWebsite').value = ''; + document.getElementById('createProducts').value = ''; + document.getElementById('createAddress').value = ''; + + document.getElementById('updateName').value = ''; + document.getElementById('updateField').value = ''; + document.getElementById('updateValue').value = ''; + + document.getElementById('deleteName').value = ''; + document.getElementById('deleteConfirm').checked = false; + const deleteBtn = document.getElementById('deleteBtn'); + deleteBtn.disabled = true; + deleteBtn.classList.add('opacity-50', 'cursor-not-allowed'); +} + +// Modal functions +function closeAIModal() { + document.getElementById('aiModal').classList.add('hidden'); +} + +// Navigation function +function goHome() { + window.location.href = 'index.html'; +} \ No newline at end of file diff --git a/assets/js/utils.js b/assets/js/utils.js new file mode 100644 index 0000000..7789e77 --- /dev/null +++ b/assets/js/utils.js @@ -0,0 +1,634 @@ +/** + * Universal Utilities for All AI Agent Pages + * Compatible with: Task Agent, Email Agent, Calendar Agent, Trade Intelligence + * Enhanced with offline handling, performance optimizations, and better error handling + */ + +// Create Utils object if it doesn't exist +window.Utils = window.Utils || {}; + +// Time utilities +Utils.time = { + updateLastUpdated: function(elementId) { + const element = document.getElementById(elementId); + if (element) { + element.textContent = new Date().toLocaleTimeString(); + } + }, + + formatTimestamp: function(date) { + if (!date) return 'Unknown'; + try { + return new Date(date).toLocaleString(); + } catch (e) { + return date; + } + } +}; + +// Form utilities +Utils.form = { + setInputValue: function(elementId, value) { + const element = document.getElementById(elementId); + if (element) { + element.value = value; + } + }, + + getInputValue: function(elementId) { + const element = document.getElementById(elementId); + return element ? element.value : ''; + }, + + clearInput: function(elementId) { + const element = document.getElementById(elementId); + if (element) { + element.value = ''; + } + } +}; + +// Button utilities +Utils.button = { + setLoading: function(elementId, text, iconClass) { + const element = document.getElementById(elementId); + if (element) { + element.innerHTML = ` ${text}`; + element.disabled = true; + } + }, + + restore: function(elementId, text, iconClass) { + const element = document.getElementById(elementId); + if (element) { + element.innerHTML = ` ${text}`; + element.disabled = false; + } + } +}; + +// Event utilities +Utils.events = { + onEnterKey: function(elementId, callback) { + const element = document.getElementById(elementId); + if (element) { + element.addEventListener('keypress', function(e) { + if (e.key === 'Enter') { + callback(); + } + }); + } + } +}; + +// Stats utilities +Utils.stats = { + increment: function(statName) { + console.log('Incrementing stat:', statName); + const element = document.getElementById(statName); + if (element) { + const currentValue = parseInt(element.textContent) || 0; + element.textContent = currentValue + 1; + } + }, + + update: function(statName, value) { + const element = document.getElementById(statName); + if (element) { + element.textContent = value; + } + } +}; + +// Enhanced API utilities with offline handling +Utils.api = { + // Enhanced request with better offline detection + async request(url, options = {}) { + try { + console.log('🔄 Making API request to:', url); + + // Check if browser is online + if (!navigator.onLine) { + throw new Error('OFFLINE'); + } + + const defaultOptions = { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + // Add timeout to detect slow/failed connections + signal: AbortSignal.timeout(30000) // 30 second timeout + }; + + const response = await fetch(url, { ...defaultOptions, ...options }); + + if (!response.ok) { + throw new Error(`HTTP_${response.status}`); + } + + const data = await response.json(); + console.log('✅ API request successful'); + return data; + + } catch (error) { + console.error('❌ API Request failed:', error); + + // Handle different types of errors with user-friendly messages + let userMessage = ''; + let errorType = 'error'; + + if (error.message === 'OFFLINE' || !navigator.onLine) { + userMessage = '📱 You appear to be offline. Please check your internet connection and try again.'; + errorType = 'warning'; + } else if (error.name === 'TimeoutError' || error.message.includes('timeout')) { + userMessage = '⏱️ Request timed out. The server may be busy. Please try again.'; + errorType = 'warning'; + } else if (error.message.includes('Failed to fetch')) { + userMessage = '🌐 Unable to connect to server. Please check your connection and try again.'; + errorType = 'warning'; + } else if (error.message.includes('HTTP_5')) { + userMessage = '🔧 Server error (5xx). Please try again in a few moments.'; + errorType = 'error'; + } else if (error.message.includes('HTTP_4')) { + userMessage = '⚠️ Request error (4xx). Please check your input and try again.'; + errorType = 'warning'; + } else { + userMessage = '❌ Something went wrong. Please try again.'; + errorType = 'error'; + } + + // Show user-friendly toast + Utils.toast.show(userMessage, errorType); + + // Re-throw for handling by calling function + throw error; + } + }, + + // Quick method for GET requests with parameters + async get(url, params = {}) { + const urlWithParams = new URL(url); + Object.keys(params).forEach(key => { + if (params[key] !== undefined && params[key] !== null) { + urlWithParams.searchParams.append(key, params[key]); + } + }); + + return this.request(urlWithParams.toString()); + }, + + // Test connection method + async testConnection() { + try { + await fetch('https://httpbin.org/status/200', { + method: 'HEAD', + signal: AbortSignal.timeout(5000) + }); + return true; + } catch (error) { + return false; + } + } +}; + +// Network status detection +Utils.network = { + isOnline: navigator.onLine, + + init() { + // Listen for online/offline events + window.addEventListener('online', () => { + this.isOnline = true; + Utils.toast.success('🌐 Connection restored!'); + console.log('✅ Back online'); + }); + + window.addEventListener('offline', () => { + this.isOnline = false; + Utils.toast.warning('📱 You are now offline. Some features may not work.'); + console.log('⚠️ Gone offline'); + }); + }, + + // Check if we should even attempt API calls + canMakeRequests() { + if (!this.isOnline) { + Utils.toast.warning('📱 No internet connection. Please check your network.'); + return false; + } + return true; + } +}; + +// Performance utilities +Utils.performance = { + // Debounce function for search inputs (prevents too many API calls) + debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + }, + + // Throttle function for scroll events + throttle(func, limit) { + let inThrottle; + return function() { + const args = arguments; + const context = this; + if (!inThrottle) { + func.apply(context, args); + inThrottle = true; + setTimeout(() => inThrottle = false, limit); + } + }; + }, + + // Optimize search inputs automatically + optimizeSearchInputs() { + const searchInputs = document.querySelectorAll('#query, #searchQuery, #taskSearchInput, #taskCommand, #calendarCommand'); + + searchInputs.forEach(input => { + if (input) { + // Remove any existing event listeners to avoid duplicates + const newInput = input.cloneNode(true); + input.parentNode.replaceChild(newInput, input); + + // Add optimized event listener + newInput.addEventListener('input', this.debounce((e) => { + // Only trigger if user has typed something meaningful + if (e.target.value.length >= 2) { + console.log('Optimized search triggered for:', e.target.value); + } + }, 300)); + } + }); + } +}; + +// Modal utilities +Utils.modal = { + showAI: function(content) { + const modal = document.getElementById('aiModal'); + const responseDiv = document.getElementById('aiResponse'); + if (modal && responseDiv) { + responseDiv.innerHTML = content; + modal.classList.remove('hidden'); + } + }, + + hideAI: function() { + const modal = document.getElementById('aiModal'); + if (modal) { + modal.classList.add('hidden'); + } + }, + + show: function(modalId, content) { + const modal = document.getElementById(modalId); + if (modal) { + if (content) { + const contentDiv = modal.querySelector('.modal-content') || + modal.querySelector('#aiResponse') || + modal.querySelector('.p-6:last-child'); + if (contentDiv) { + contentDiv.innerHTML = content; + } + } + modal.classList.remove('hidden'); + } + }, + + hide: function(modalId) { + const modal = document.getElementById(modalId); + if (modal) { + modal.classList.add('hidden'); + } + } +}; + +// Enhanced toast notifications +Utils.toast = { + show: function(message, type = 'info') { + // Remove existing toasts + document.querySelectorAll('.utils-toast').forEach(toast => toast.remove()); + + const toast = document.createElement('div'); + toast.className = 'utils-toast fixed top-4 right-4 z-50 max-w-sm transform transition-all duration-300 translate-x-full'; + + const styles = { + success: { bg: 'bg-green-500', icon: 'fas fa-check-circle' }, + error: { bg: 'bg-red-500', icon: 'fas fa-exclamation-circle' }, + warning: { bg: 'bg-yellow-500', icon: 'fas fa-exclamation-triangle' }, + info: { bg: 'bg-blue-500', icon: 'fas fa-info-circle' } + }; + + const style = styles[type] || styles.info; + + toast.innerHTML = ` +
+ + ${message} + +
+ `; + + document.body.appendChild(toast); + + // Animate in + requestAnimationFrame(() => { + toast.classList.remove('translate-x-full'); + }); + + // Auto remove after 5 seconds + setTimeout(() => { + if (toast.parentElement) { + toast.classList.add('translate-x-full'); + setTimeout(() => toast.remove(), 300); + } + }, 5000); + }, + + success: function(message) { this.show(message, 'success'); }, + error: function(message) { this.show(message, 'error'); }, + warning: function(message) { this.show(message, 'warning'); }, + info: function(message) { this.show(message, 'info'); } +}; + +// Navigation utilities +Utils.navigation = { + goHome: function() { + window.location.href = 'index.html'; + }, + + goToPage: function(page) { + window.location.href = page; + } +}; + +// Validation utilities +Utils.validation = { + isEmail: function(email) { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }, + + isEmpty: function(value) { + return !value || value.toString().trim().length === 0; + }, + + isValidDate: function(date) { + return date instanceof Date && !isNaN(date); + } +}; + +// Loading utilities +Utils.loading = { + show: function(elementId, message = 'Loading...') { + const element = document.getElementById(elementId); + if (element) { + element.classList.remove('hidden'); + const messageEl = element.querySelector('.loading-message'); + if (messageEl) { + messageEl.textContent = message; + } + } + }, + + hide: function(elementId) { + const element = document.getElementById(elementId); + if (element) { + element.classList.add('hidden'); + } + }, + + toggle: function(elementId, show, message) { + if (show) { + this.show(elementId, message); + } else { + this.hide(elementId); + } + } +}; + +// Storage utilities (for local data) +Utils.storage = { + set: function(key, value) { + try { + localStorage.setItem(key, JSON.stringify(value)); + } catch (e) { + console.warn('localStorage not available, using memory storage'); + this._memoryStorage = this._memoryStorage || {}; + this._memoryStorage[key] = value; + } + }, + + get: function(key, defaultValue = null) { + try { + const item = localStorage.getItem(key); + return item ? JSON.parse(item) : defaultValue; + } catch (e) { + this._memoryStorage = this._memoryStorage || {}; + return this._memoryStorage[key] || defaultValue; + } + }, + + remove: function(key) { + try { + localStorage.removeItem(key); + } catch (e) { + if (this._memoryStorage) { + delete this._memoryStorage[key]; + } + } + } +}; + +// Formatting utilities +Utils.format = { + currency: function(amount, currency = 'USD') { + try { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: currency + }).format(amount); + } catch (e) { + return `$${amount}`; + } + }, + + number: function(num, decimals = 0) { + return Number(num).toFixed(decimals); + }, + + date: function(date, format = 'short') { + try { + return new Date(date).toLocaleDateString('en-US', { + dateStyle: format + }); + } catch (e) { + return date; + } + }, + + time: function(date) { + try { + return new Date(date).toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit' + }); + } catch (e) { + return date; + } + } +}; + +// Searchable dropdown utility for countries +Utils.dropdown = { + createSearchableCountryDropdown: function(selectElement, countries) { + if (!selectElement) return; + + const container = selectElement.parentNode; + const wrapper = document.createElement('div'); + wrapper.className = 'relative'; + + // Create input field + const input = document.createElement('input'); + input.type = 'text'; + input.placeholder = 'Type to search countries...'; + input.className = selectElement.className; + input.id = selectElement.id + '_input'; + + // Create dropdown + const dropdown = document.createElement('div'); + dropdown.className = 'absolute z-10 w-full bg-white border border-gray-300 rounded-lg shadow-lg max-h-48 overflow-y-auto hidden'; + dropdown.style.top = '100%'; + + // Create options + const createOptions = (filteredCountries) => { + dropdown.innerHTML = ''; + + if (filteredCountries.length === 0) { + dropdown.innerHTML = '
No countries found
'; + return; + } + + filteredCountries.forEach(country => { + const option = document.createElement('div'); + option.className = 'p-3 hover:bg-gray-100 cursor-pointer text-sm border-b border-gray-100 last:border-b-0'; + option.textContent = country; + option.addEventListener('click', () => { + input.value = country; + selectElement.value = country; + dropdown.classList.add('hidden'); + + // Trigger change event + const event = new Event('change', { bubbles: true }); + selectElement.dispatchEvent(event); + }); + dropdown.appendChild(option); + }); + }; + + // Initialize with all countries + createOptions(countries); + + // Handle input changes + input.addEventListener('input', (e) => { + const query = e.target.value.toLowerCase(); + const filtered = countries.filter(country => + country.toLowerCase().includes(query) + ); + createOptions(filtered); + dropdown.classList.remove('hidden'); + }); + + // Show dropdown on focus + input.addEventListener('focus', () => { + dropdown.classList.remove('hidden'); + }); + + // Hide dropdown when clicking outside + document.addEventListener('click', (e) => { + if (!wrapper.contains(e.target)) { + dropdown.classList.add('hidden'); + } + }); + + // Build wrapper + wrapper.appendChild(input); + wrapper.appendChild(dropdown); + + // Replace original select + container.replaceChild(wrapper, selectElement); + + // Keep original select hidden for form submission + selectElement.style.display = 'none'; + wrapper.appendChild(selectElement); + + return wrapper; + } +}; + +// Global convenience functions for backward compatibility +function goHome() { + Utils.navigation.goHome(); +} + +function updateLastUpdated(elementId) { + Utils.time.updateLastUpdated(elementId); +} + +function showToast(message, type = 'info') { + Utils.toast.show(message, type); +} + +// Global error handler +window.addEventListener('error', function(event) { + console.error('Global error caught:', event.error); + Utils.toast.error('An unexpected error occurred. Please refresh the page.'); +}); + +// Global unhandled promise rejection handler +window.addEventListener('unhandledrejection', function(event) { + console.error('Unhandled promise rejection:', event.reason); + Utils.toast.error('A network or processing error occurred.'); +}); + +// Export for global use +window.goHome = goHome; +window.updateLastUpdated = updateLastUpdated; +window.showToast = showToast; + +// Initialize everything when DOM loads +document.addEventListener('DOMContentLoaded', function() { + // Initialize network detection + Utils.network.init(); + + // Apply performance optimizations + setTimeout(() => { + Utils.performance.optimizeSearchInputs(); + console.log('✅ Performance optimizations applied'); + }, 1000); + + // Update any "Last updated" timestamps on the page + const timestampElements = document.querySelectorAll('[id*="last"], [id*="Last"], [id*="update"], [id*="Update"], [id*="sync"], [id*="Sync"]'); + timestampElements.forEach(element => { + if (element.id) { + Utils.time.updateLastUpdated(element.id); + } + }); + + console.log('✅ Universal Utils.js loaded and initialized'); +}); + +console.log('✅ Universal Utils.js loaded successfully'); \ No newline at end of file diff --git a/calendar-agent.html b/calendar-agent.html new file mode 100644 index 0000000..2b1ea6d --- /dev/null +++ b/calendar-agent.html @@ -0,0 +1,400 @@ + + + + + + Calendar Agent - AI Calendar Management + + + + + + + + + + + + + + +
+ + +
+

AI Calendar Management

+

+ Smart scheduling, optimal time finding, and intelligent meeting management powered by AI. +

+
+ + +
+
+
+ +
+

Calendar AI Command

+
+ +
+
+ +
+ +
+ + +
+ Quick commands: + + + +
+
+ + +
+
+
+
+ +
+
5
+
Today
+
+
+ +
+
+
+ +
+
2:30 PM
+
Next
+
+
+ +
+
+
+ +
+
3
+
Free Slots
+
+
+ +
+
+
+ +
+
12
+
AI Scheduled
+
+
+
+ + +
+ + +
+ + +
+
+

December 2024

+
+ + +
+
+ +
+
Sun
+
Mon
+
Tue
+
Wed
+
Thu
+
Fri
+
Sat
+
+ +
+ +
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
+
+ + +
+
+

Today's Schedule

+ Monday, Dec 2, 2024 +
+ +
+ +
+
+
+
+ Team Standup + Live +
+ 9:00 - 9:30 AM +
+

Daily sync with development team

+
+ 8 attendees + Zoom Meeting +
+
+ + +
+
+ Client Presentation + 2:30 - 3:30 PM +
+

Q4 strategy review with ABC Corp

+
+ 5 attendees + Conference Room A +
+
+ + +
+
+ +
+
+ Budget Review + 4:00 - 5:00 PM +
+

Monthly financial planning session

+
+ 3 attendees + Teams Meeting +
+
+ + +
+
+ Focus Time + 10:00 - 12:00 PM +
+

2-hour block reserved for deep work

+
+ AI Protected Time +
+
+
+
+
+ + +
+ + +
+

Quick Actions

+
+ + + + +
+
+ + +
+

Time Analysis

+
+
+

Meeting Load

+
+
+
+

65% of day in meetings

+
+
+

Focus Time

+
+
+
+

2.8 hours available

+
+
+

Efficiency Score

+

87%

+

Above average

+
+
+
+ + +
+

AI Insights

+
+
+

⚠️ Potential Conflict

+

Tomorrow 3PM: Two meetings scheduled

+
+
+

💡 Suggestion

+

Move standup to 8:30 AM for better flow

+
+
+

✨ Opportunity

+

Friday 2-4 PM available for deep work

+
+
+
+ + +
+

Recent AI Actions

+
+
+
+ +
+
+

Scheduled team meeting

+

3 minutes ago

+
+
+ +
+
+ +
+
+

Found optimal meeting time

+

8 minutes ago

+
+
+ +
+
+ +
+
+

Protected focus time

+

15 minutes ago

+
+
+
+
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/email-agent.html b/email-agent.html new file mode 100644 index 0000000..f232e9f --- /dev/null +++ b/email-agent.html @@ -0,0 +1,253 @@ + + + + + + Email Agent - AI Email Management + + + + + + + + + + + + + + + +
+ + +
+

AI Email Management

+
+ + +
+
+
+ +
+

Email Summary Search

+
+ +
+
+ +
+ +
+ + +
+ Filter by: + + + + +
+
+ + +
+
+
+
+ +
+
0
+
Total Items
+
+
+ +
+
+
+ +
+
0
+
High Priority
+
+
+ +
+
+
+ +
+
0
+
Processed
+
+
+ +
+
+
+ +
+
0
+
AI Analyzed
+
+
+
+ + +
+ + +
+
+
+

Summarised Emails

+
+ + +
+
+ + + + + +
+ +
+ + +
+
+ +
+

No emails found

+

Your processed emails will appear here

+ +
+
+
+ + +
+ + +
+

Quick Actions

+
+ + + + +
+
+ + + + +
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..c49c353 --- /dev/null +++ b/index.html @@ -0,0 +1,250 @@ + + + + + + Victor AI - AI Services Hub + + + + + + + + + + + + + + + +
+ + +
+
+
+ +
+
+

+ Welcome to Victor AI +

+ + + + + +
+ + +
+

AI Agents

+
+ + +
+
+
+ +
+

Trade Intelligence

+

Find exporters, importers, and trade data worldwide

+
+
50K+
+
Companies
+
+ +
+
+ + +
+
+
+ +
+

Email Agent

+

Smart email management and summarization

+
+
23
+
Unread Emails
+
+ +
+
+ + +
+
+
+ +
+

Calendar Agent

+

Intelligent scheduling and meeting management

+
+
2:30 PM
+
Next Meeting
+
+ +
+
+ + +
+
+
+ +
+

Task Agent

+

Smart task management and prioritization

+
+
4
+
Due Today
+
+ +
+
+
+
+
+ + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fe0dbb7299b5796cf557003caa79da5906ae694b GIT binary patch literal 7435 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 + + + + + Task Agent - AI Task Management + + + + + + + + + + + + + + +
+ + +
+

AI Task Management

+
+ + +
+
+
+ +
+

Task AI Command & Search

+
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+
+ + +
+ Quick commands: + + + +
+ + +
+ Filter by status: + + + + +
+
+ + +
+
+
+
+ +
+
0
+
Overdue
+
+
+ +
+
+
+ +
+
0
+
Due Today
+
+
+ +
+
+
+ +
+
0
+
Total
+
+
+ +
+
+
+ +
+
0
+
Completed
+
+
+
+ + + + + +
+ + +
+
+ + +
+
+

+
+ To Do +

+ 0 +
+ +
+ +
+
+ + +
+
+

+
+ In Progress +

+ 0 +
+ +
+ +
+
+ + +
+
+

+
+ Done +

+ 0 +
+ +
+ +
+
+
+
+ + +
+ + +
+

Quick Actions

+
+ + + + +
+
+ + +
+

Data Source

+
+
+ +
+
+

Sample Data

+

Demo Mode Active

+
+
+
+ +
+ + +
+

Productivity Insights

+
+
+

Completion Rate

+
+
+
+

87% (Above average)

+
+
+

Focus Score

+
8.2/10
+

Excellent focus

+
+
+

Avg Task Time

+

2.3 days

+

15% faster this week

+
+
+
+ + +
+

AI Recommendations

+
+
+

🚨 Urgent Action

+

Security audit is 2 days overdue

+
+
+

💡 Suggestion

+

Break large tasks into smaller chunks

+
+
+

✨ Optimization

+

Batch similar tasks for efficiency

+
+
+
+ + +
+

Recent AI Actions

+
+
+
+ +
+
+

Auto-prioritized 5 tasks

+

3 minutes ago

+
+
+ +
+
+ +
+
+

Marked deployment complete

+

2 hours ago

+
+
+ +
+
+ +
+
+

Created 3 subtasks

+

4 hours ago

+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trade-intelligence.html b/trade-intelligence.html new file mode 100644 index 0000000..9398592 --- /dev/null +++ b/trade-intelligence.html @@ -0,0 +1,504 @@ + + + + + + Trade Intelligence - AI Trade Management + + + + + + + + + + + + + + +
+ + +
+

AI Trade Intelligence

+
+ + +
+
+
+ +
+

Trade AI Command

+
+ + +
+
+ +
+ +
+ + +
+
+ Quick searches: + + + + +
+
+ + +
+
+ +

Advanced Filters

+
+ +
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+ + + +
+
+
+ + + + + +
+ + +
+ + +
+ + +
+ + +
+

Quick Actions

+
+ + + + +
+
+
+
+
+ + + + +
+ +
+ + + + + +
+ +
+ + + + + + + + + + \ No newline at end of file