Jake Vanderwerf
2025-11-04 42fa8304ddb811b0f725f245130f70c0f5e86a6c
assets/js/dash/Integrations.js
@@ -53,11 +53,21 @@
      const error = urlParams.get('error');
      if (success) {
         this.showNotification(success, 'success');
         this.showNotification(success, 'success', 5000);
         // Clean URL without reloading
         this.cleanURL();
         // Refresh the integration status display
         const forms = document.querySelectorAll('form.integration');
         forms.forEach(form => {
            // Update UI to show connected state
            this.updateUI(form, 'connected');
         });
      } else if (error) {
         this.showNotification(error, 'error');
         this.showNotification(error, 'error', 8000);
         // Clean URL without reloading
         this.cleanURL();
      }
@@ -76,26 +86,42 @@
   /**
    * Show notification message
    */
   showNotification(message, type = 'info') {
      // If you have a notification system, use it
   showNotification(message, type = 'info', duration = 5000) {
      // Find or create notification container
      let container = document.querySelector('.integration-status-message');
      if (!container) {
         // Create notification container
         container = document.createElement('div');
         container.className = 'integration-status-message';
         // Insert at top of main content or integrations container
         const target = document.querySelector('.integration-settings') ||
            document.querySelector('main') ||
            document.body;
         target.insertBefore(container, target.firstChild);
      }
      // Update content and type
      container.textContent = message;
      container.className = `integration-status-message ${type}`;
      // Clear any existing timeout
      if (this.notificationTimeout) {
         clearTimeout(this.notificationTimeout);
      }
      // Auto-hide after duration
      if (duration > 0) {
         this.notificationTimeout = setTimeout(() => {
            container.className = 'integration-status-message';
            container.textContent = '';
         }, duration);
      }
      // Also use popup if available
      if (this.popup) {
         this.addPopup(message, type === 'error' ? 5000 : 3000);
      } else {
         // Fallback to console or alert
         console.log(`[${type}]`, message);
         // Update any status elements on the page
         const statusElements = document.querySelectorAll('.integration-status-message');
         statusElements.forEach(el => {
            el.textContent = message;
            el.className = `integration-status-message ${type}`;
            // Auto-hide after delay
            setTimeout(() => {
               el.textContent = '';
               el.className = 'integration-status-message';
            }, 5000);
         });
         this.addPopup(message, duration);
      }
   }
@@ -122,9 +148,21 @@
   }
   clickHandler(e) {
      // // Check for OAuth authorization link
      // if (e.target.classList.contains('jvb-oauth-connect') ||
      //    e.target.closest('.jvb-oauth-connect')) {
      //    e.preventDefault();
      //    const link = e.target.classList.contains('jvb-oauth-connect')
      //       ? e.target
      //       : e.target.closest('.jvb-oauth-connect');
      //    return this.handleOAuthClick(link);
      // }
      // Existing integration form handling
      if (!e.target.closest(this.selectors.form)) {
         return;
      }
      console.log('Clicked!');
      if (e.target.tagName === 'BUTTON' || e.target.closest('button')) {
         e.preventDefault();
@@ -159,6 +197,90 @@
      return this.forms.get(service)??false;
   }
   /**
    * Handle OAuth authorization link clicks
    * Opens OAuth in a popup window with proper handling
    */
   handleOAuthClick(link) {
      const service = link.dataset.service;
      const href = link.href;
      // Calculate center position for popup
      const width = 600;
      const height = 700;
      const left = (screen.width - width) / 2;
      const top = (screen.height - height) / 2;
      // Show loading notification
      this.showNotification('Opening authorization window...', 'info');
      // Add loading state to the link
      link.classList.add('loading');
      link.setAttribute('aria-busy', 'true');
      // Open OAuth in popup
      const popup = window.open(
         href,
         'oauth_' + service,
         `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no,location=yes,status=yes,resizable=yes`
      );
      if (!popup) {
         // Popup was blocked
         this.showNotification('Popup was blocked. Please allow popups and try again.', 'error');
         link.classList.remove('loading');
         link.removeAttribute('aria-busy');
         return true; // Allow default behavior as fallback
      }
      // Focus the popup
      popup.focus();
      // Update notification
      this.showNotification('Waiting for authorization...', 'info');
      // Poll for popup close
      const pollTimer = setInterval(() => {
         try {
            if (popup.closed) {
               clearInterval(pollTimer);
               // Remove loading state
               link.classList.remove('loading');
               link.removeAttribute('aria-busy');
               // Show checking notification
               this.showNotification('Checking authorization status...', 'info');
               // Wait a moment for redirect to complete, then check for messages
               setTimeout(() => {
                  this.checkForOAuthMessages();
                  // If no messages found, reload to get updated connection status
                  setTimeout(() => {
                     const urlParams = new URLSearchParams(window.location.search);
                     if (!urlParams.has('success') && !urlParams.has('error')) {
                        // No messages in URL, reload to check server-side status
                        window.location.reload();
                     }
                  }, 500);
               }, 500);
            }
         } catch (error) {
            // Ignore cross-origin errors during polling
         }
      }, 500);
      // Safety timeout - stop polling after 5 minutes
      setTimeout(() => {
         clearInterval(pollTimer);
         link.classList.remove('loading');
         link.removeAttribute('aria-busy');
      }, 300000);
      return false; // Prevent default link behavior
   }
   async handleAction(input) {
      const form = input.closest('form');
      const service = form.dataset.service;