Spaces:
Running
Running
| // HBU Asset Recovery Command Center - Main JavaScript | |
| // Initialize app | |
| document.addEventListener('DOMContentLoaded', function() { | |
| feather.replace(); | |
| initializeApp(); | |
| }); | |
| // App initialization | |
| function initializeApp() { | |
| // Set initial section | |
| showSection('dashboard'); | |
| // Start periodic updates | |
| setInterval(updateStats, 30000); // Update stats every 30 seconds | |
| setInterval(checkNewNotifications, 10000); // Check notifications every 10 seconds | |
| // Initialize tooltips | |
| initializeTooltips(); | |
| // Add keyboard shortcuts | |
| initializeKeyboardShortcuts(); | |
| } | |
| // Section management | |
| function showSection(sectionId) { | |
| // Hide all sections | |
| const sections = document.querySelectorAll('.section'); | |
| sections.forEach(section => { | |
| section.classList.add('hidden'); | |
| }); | |
| // Show selected section | |
| const targetSection = document.getElementById(sectionId); | |
| if (targetSection) { | |
| targetSection.classList.remove('hidden'); | |
| } | |
| // Update nav items | |
| const navItems = document.querySelectorAll('.nav-item'); | |
| navItems.forEach(item => { | |
| item.classList.remove('text-primary-700', 'bg-primary-50'); | |
| item.classList.add('text-gray-700'); | |
| }); | |
| // Highlight active nav item | |
| const activeNavItem = document.querySelector(`[onclick="showSection('${sectionId}')"]`); | |
| if (activeNavItem) { | |
| activeNavItem.classList.remove('text-gray-700'); | |
| activeNavItem.classList.add('text-primary-700', 'bg-primary-50'); | |
| } | |
| // Re-render feather icons | |
| feather.replace(); | |
| } | |
| // Toggle notifications panel | |
| function toggleNotifications() { | |
| const panel = document.getElementById('notificationsPanel'); | |
| if (panel) { | |
| panel.classList.toggle('translate-x-full'); | |
| } | |
| } | |
| // Modal management | |
| function openNewLeadModal() { | |
| const modal = document.getElementById('newLeadModal'); | |
| if (modal) { | |
| modal.classList.remove('hidden'); | |
| document.body.style.overflow = 'hidden'; | |
| } | |
| } | |
| function closeNewLeadModal() { | |
| const modal = document.getElementById('newLeadModal'); | |
| if (modal) { | |
| modal.classList.add('hidden'); | |
| document.body.style.overflow = 'auto'; | |
| } | |
| } | |
| // Add new lead | |
| function addNewLead(event) { | |
| event.preventDefault(); | |
| // Show loading state | |
| const submitButton = event.target.querySelector('button[type="submit"]'); | |
| const originalText = submitButton.innerHTML; | |
| submitButton.innerHTML = '<div class="spinner inline-block w-4 h-4 mr-2"></div> Adding...'; | |
| submitButton.disabled = true; | |
| // Simulate API call | |
| setTimeout(() => { | |
| // Reset form | |
| event.target.reset(); | |
| // Close modal | |
| closeNewLeadModal(); | |
| // Show success notification | |
| showNotification('Lead added successfully!', 'success'); | |
| // Reset button | |
| submitButton.innerHTML = originalText; | |
| submitButton.disabled = false; | |
| // Update lead count | |
| updateLeadCount(); | |
| }, 1500); | |
| } | |
| // Skip trace functionality | |
| function performSkipTrace(event) { | |
| event.preventDefault(); | |
| const formData = { | |
| firstName: document.getElementById('firstName').value, | |
| lastName: document.getElementById('lastName').value, | |
| phoneNumber: document.getElementById('phoneNumber').value, | |
| address: document.getElementById('address').value | |
| }; | |
| // Show loading state | |
| const submitButton = event.target.querySelector('button[type="submit"]'); | |
| const originalText = submitButton.innerHTML; | |
| submitButton.innerHTML = '<div class="spinner inline-block w-4 h-4 mr-2"></div> Searching...'; | |
| submitButton.disabled = true; | |
| // Simulate API call | |
| setTimeout(() => { | |
| // Simulate found results | |
| const results = { | |
| status: 'found', | |
| phone: '(405) 555-' + Math.floor(Math.random() * 9000 + 1000), | |
| email: formData.firstName.toLowerCase() + '.' + formData.lastName.toLowerCase() + '@email.com', | |
| address: formData.address || Math.floor(Math.random() * 9000 + 1000) + ' Main St, Oklahoma City, OK 73102', | |
| relatives: ['John Doe', 'Jane Smith'] | |
| }; | |
| // Show results | |
| showSkipTraceResults(formData, results); | |
| // Reset button | |
| submitButton.innerHTML = originalText; | |
| submitButton.disabled = false; | |
| // Show notification | |
| showNotification('Skip trace completed successfully!', 'success'); | |
| }, 2000); | |
| } | |
| function showSkipTraceResults(searchData, results) { | |
| // This would normally update the UI with results | |
| console.log('Skip trace results:', results); | |
| // Add to recent results section | |
| const resultsContainer = document.querySelector('#skiptrace .bg-white.rounded-xl:last-child .space-y-4'); | |
| if (resultsContainer) { | |
| const resultHtml = ` | |
| <div class="p-4 bg-green-50 border border-green-200 rounded-lg success-animation"> | |
| <div class="flex items-start justify-between mb-2"> | |
| <div> | |
| <p class="font-medium text-gray-900">${searchData.firstName} ${searchData.lastName}</p> | |
| <p class="text-sm text-gray-600">Found: Just now</p> | |
| </div> | |
| <span class="px-2 py-1 text-xs bg-green-100 text-green-800 rounded-full">Complete</span> | |
| </div> | |
| <div class="space-y-1 text-sm text-gray-700"> | |
| <p>📞 ${results.phone}</p> | |
| <p>📧 ${results.email}</p> | |
| <p>📍 ${results.address}</p> | |
| </div> | |
| </div> | |
| `; | |
| resultsContainer.insertAdjacentHTML('afterbegin', resultHtml); | |
| } | |
| } | |
| // Communication functions | |
| function makeAICall() { | |
| const phoneNumber = prompt('Enter phone number to call:'); | |
| if (phoneNumber) { | |
| showNotification(`Initiating AI call to ${phoneNumber}...`, 'info'); | |
| // Simulate call initiation | |
| setTimeout(() => { | |
| showNotification('AI call initiated successfully!', 'success'); | |
| updateCallStats(); | |
| }, 1000); | |
| } | |
| } | |
| function sendSMS() { | |
| const phoneNumber = prompt('Enter phone number for SMS:'); | |
| const message = prompt('Enter message:'); | |
| if (phoneNumber && message) { | |
| showNotification(`Sending SMS to ${phoneNumber}...`, 'info'); | |
| setTimeout(() => { | |
| showNotification('SMS sent successfully!', 'success'); | |
| }, 1000); | |
| } | |
| } | |
| function sendEmail() { | |
| const email = prompt('Enter email address:'); | |
| const subject = prompt('Enter subject:'); | |
| const message = prompt('Enter message:'); | |
| if (email && subject && message) { | |
| showNotification(`Sending email to ${email}...`, 'info'); | |
| setTimeout(() => { | |
| showNotification('Email sent successfully!', 'success'); | |
| }, 1500); | |
| } | |
| } | |
| // AI Assistant chat | |
| function sendAIMessage(event) { | |
| event.preventDefault(); | |
| const input = document.getElementById('aiInput'); | |
| const message = input.value.trim(); | |
| if (!message) return; | |
| // Add user message to chat | |
| addChatMessage(message, 'user'); | |
| // Clear input | |
| input.value = ''; | |
| // Show typing indicator | |
| showTypingIndicator(); | |
| // Simulate AI response | |
| setTimeout(() => { | |
| removeTypingIndicator(); | |
| const response = generateAIResponse(message); | |
| addChatMessage(response, 'assistant'); | |
| }, 1500); | |
| } | |
| function addChatMessage(message, sender) { | |
| const chatContainer = document.getElementById('chatMessages'); | |
| if (!chatContainer) return; | |
| const messageHtml = sender === 'user' ? ` | |
| <div class="flex items-start gap-3 justify-end"> | |
| <div class="chat-message user px-4 py-2 rounded-lg"> | |
| <p class="text-sm">${message}</p> | |
| </div> | |
| <img src="https://static.photos/people/40x40/42" alt="User" class="w-8 h-8 rounded-full"> | |
| </div> | |
| ` : ` | |
| <div class="flex items-start gap-3"> | |
| <div class="p-2 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full"> | |
| <i data-feather="cpu" class="w-4 h-4 text-white"></i> | |
| </div> | |
| <div class="chat-message assistant px-4 py-2 rounded-lg"> | |
| <p class="text-sm">${message}</p> | |
| </div> | |
| </div> | |
| `; | |
| chatContainer.insertAdjacentHTML('beforeend', messageHtml); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| feather.replace(); | |
| } | |
| function showTypingIndicator() { | |
| const chatContainer = document.getElementById('chatMessages'); | |
| if (!chatContainer) return; | |
| const typingHtml = ` | |
| <div id="typingIndicator" class="flex items-start gap-3"> | |
| <div class="p-2 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full"> | |
| <i data-feather="cpu" class="w-4 h-4 text-white"></i> | |
| </div> | |
| <div class="chat-message assistant px-4 py-2 rounded-lg"> | |
| <div class="flex gap-1"> | |
| <div class="w-2 h-2 bg-gray-500 rounded-full animate-bounce" style="animation-delay: 0ms"></div> | |
| <div class="w-2 h-2 bg-gray-500 rounded-full animate-bounce" style="animation-delay: 150ms"></div> | |
| <div class="w-2 h-2 bg-gray-500 rounded-full animate-bounce" style="animation-delay: 300ms"></div> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| chatContainer.insertAdjacentHTML('beforeend', typingHtml); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| feather.replace(); | |
| } | |
| function removeTypingIndicator() { | |
| const indicator = document.getElementById('typingIndicator'); | |
| if (indicator) { | |
| indicator.remove(); | |
| } | |
| } | |
| function generateAIResponse(message) { | |
| const responses = { | |
| 'skip trace': 'I can help you skip trace individuals. Please provide the person\'s name, and any known phone numbers or addresses. I\'ll search through multiple databases to locate current contact information.', | |
| 'legal': 'Oklahoma unclaimed property is governed by 12 O.S. § 772. The Unclaimed Property Act requires holders to report and remit unclaimed property to the State Treasurer after a specified dormancy period.', | |
| 'call script': 'Here\'s a professional opening script: "Hello, my name is [Your Name] from HBU Asset Recovery. I\'m calling because we\'ve located potential unclaimed funds that may belong to you. Would you have a moment to discuss this opportunity?"', | |
| 'default': 'I understand you\'re looking for help with asset recovery. I can assist with skip tracing, legal research, document analysis, and generating outreach materials. What specific task would you like help with?' | |
| }; | |
| const lowerMessage = message.toLowerCase(); | |
| for (const [key, response] of Object.entries(responses)) { | |
| if (lowerMessage.includes(key)) { | |
| return response; | |
| } | |
| } | |
| return responses.default; | |
| } | |
| // Notification system | |
| function showNotification(message, type = 'info') { | |
| const notification = document.createElement('div'); | |
| const colors = { | |
| success: 'bg-green-500', | |
| error: 'bg-red-500', | |
| info: 'bg-blue-500', | |
| warning: 'bg-yellow-500' | |
| }; | |
| notification.className = `fixed top-20 right-4 ${colors[type]} text-white px-6 py-3 rounded-lg shadow-lg z-50 transform translate-x-full transition-transform`; | |
| notification.textContent = message; | |
| document.body.appendChild(notification); | |
| // Animate in | |
| setTimeout(() => { | |
| notification.classList.remove('translate-x-full'); | |
| }, 100); | |
| // Remove after 3 seconds | |
| setTimeout(() => { | |
| notification.classList.add('translate-x-full'); | |
| setTimeout(() => { | |
| notification.remove(); | |
| }, 300); | |
| }, 3000); | |
| } | |
| function checkNewNotifications() { | |
| // This would normally check for new notifications from the server | |
| // Simulate random notifications | |
| if (Math.random() > 0.9) { | |
| const notifications = [ | |
| 'New lead has been assigned to you', | |
| 'AI call completed with positive outcome', | |
| 'Document review required', | |
| 'Skip trace results available' | |
| ]; | |
| const randomNotification = notifications[Math.floor(Math.random() * notifications.length)]; | |
| showNotification(randomNotification, 'info'); | |
| } | |
| } | |
| // Stats updates | |
| function updateStats() { | |
| // Update dashboard stats with simulated data | |
| const stats = { | |
| leads: 247 + Math.floor(Math.random() * 10 - 5), | |
| calls: 42 + Math.floor(Math.random() * 10), | |
| traces: 18 + Math.floor(Math.random() * 5), | |
| recovered: 84500 + Math.floor(Math.random() * 5000) | |
| }; | |
| // Update stat displays | |
| const statElements = document.querySelectorAll('.text-2xl'); | |
| if (statElements.length >= 4) { | |
| statElements[0].textContent = stats.leads; | |
| statElements[1].textContent = stats.calls; | |
| statElements[2].textContent = stats.traces; | |
| statElements[3].textContent = '$' + (stats.recovered / 1000).toFixed(1) + 'K'; | |
| } | |
| } | |
| function updateLeadCount() { | |
| const leadCountElement = document.querySelector('[onclick="showSection(\'leads\')"] span'); | |
| if (leadCountElement) { | |
| const currentCount = parseInt(leadCountElement.textContent); | |
| leadCountElement.textContent = currentCount + 1; | |
| leadCountElement.classList.add('notification-badge'); | |
| setTimeout(() => { | |
| leadCountElement.classList.remove('notification-badge'); | |
| }, 500); | |
| } | |
| } | |
| function updateCallStats() { | |
| const callsElement = document.querySelectorAll('.text-2xl')[1]; | |
| if (callsElement) { | |
| const currentCalls = parseInt(callsElement.textContent); | |
| callsElement.textContent = currentCalls + 1; | |
| callsElement.parentElement.classList.add('success-animation'); | |
| } | |
| } | |
| // Utility functions | |
| function initializeTooltips() { | |
| const elements = document.querySelectorAll('[data-tooltip]'); | |
| elements.forEach(element => { | |
| element.classList.add('tooltip'); | |
| }); | |
| } | |
| function initializeKeyboardShortcuts() { | |
| document.addEventListener('keydown', function(event) { | |
| // Ctrl/Cmd + K for quick search | |
| if ((event.ctrlKey || event.metaKey) && event.key === 'k') { | |
| event.preventDefault(); | |
| const searchInput = document.querySelector('input[placeholder="Search leads..."]'); | |
| if (searchInput) { | |
| searchInput.focus(); | |
| } | |
| } | |
| // Escape to close modals | |
| if (event.key === 'Escape') { | |
| closeNewLeadModal(); | |
| const notificationsPanel = document.getElementById('notificationsPanel'); | |
| if (notificationsPanel && !notificationsPanel.classList.contains('translate-x-full')) { | |
| toggleNotifications(); | |
| } | |
| } | |
| // Number keys for navigation | |
| if (event.altKey && event.key >= '1' && event.key <= '8') { | |
| const sections = ['dashboard', 'leads', 'skiptrace', 'communications', 'tasks', 'documents', 'ai-assistant', 'settings']; | |
| const sectionIndex = parseInt(event.key) - 1; | |
| if (sections[sectionIndex]) { | |
| showSection(sections[sectionIndex]); | |
| } | |
| } | |
| }); | |
| } | |
| // Utility functions for API calls (would be implemented with actual endpoints) | |
| async function apiCall(endpoint, method = 'GET', data = null) { | |
| try { | |
| const options = { | |
| method, | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| } | |
| }; | |
| if (data) { | |
| options.body = JSON.stringify(data); | |
| } | |
| const response = await fetch(`/api/${endpoint}`, options); | |
| return await response.json(); | |
| } catch (error) { | |
| console.error('API call failed:', error); | |
| showNotification('An error occurred. Please try again.', 'error'); | |
| return null; | |
| } | |
| } | |
| // Export functions for global access | |
| window.showSection = showSection; | |
| window.toggleNotifications = toggleNotifications; | |
| window.openNewLeadModal = openNewLeadModal; | |
| window.closeNewLeadModal = closeNewLeadModal; | |
| window.addNewLead = addNewLead; | |
| window.performSkipTrace = performSkipTrace; | |
| window.makeAICall = makeAICall; | |
| window.sendSMS = sendSMS; | |
| window.sendEmail = sendEmail; | |
| window.sendAIMessage = sendAIMessage; |