← back to Designer Wallcoverings

DW-Agents/dw-agents/agent-marketing/shared-ui-components.js

363 lines

/**
 * DW-Agents Universal UI Components
 * Shared across all agents for consistency
 * Inspired by modern UI patterns from 21st.dev
 */

// Import theme manager
const { getTheme, getInlineThemeStyles } = require('./theme-manager.js');

// Get modern minimalist theme as default
const DEFAULT_THEME = getTheme('modern-minimalist');

// Brand colors from UI Manager (with modern minimalist theme)
const BRAND_COLORS = {
  primary: DEFAULT_THEME.colors.primary,
  secondary: DEFAULT_THEME.colors.secondary,
  accent: DEFAULT_THEME.colors.accent,
  success: DEFAULT_THEME.colors.success,
  warning: DEFAULT_THEME.colors.warning,
  danger: DEFAULT_THEME.colors.error,
  dark: DEFAULT_THEME.colors.neutral800,
  light: DEFAULT_THEME.colors.neutral100
};

/**
 * Universal Header with Chat + Refresh + Live Countdown
 * Returns HTML string to inject at top of every agent page
 */
function getUniversalHeader(agentName, agentPort, refreshInterval = 5) {
  return `
    <!-- Universal DW-Agents Header -->
    <div id="dw-universal-header" style="
      position: sticky;
      top: 0;
      z-index: 9999;
      background: linear-gradient(135deg, ${BRAND_COLORS.primary} 0%, ${BRAND_COLORS.secondary} 100%);
      box-shadow: 0 4px 20px rgba(0,0,0,0.15);
      padding: 15px 25px;
      margin: -20px -20px 20px -20px;
    ">
      <div style="display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;">

        <!-- Agent Info -->
        <div style="display: flex; align-items: center; gap: 15px;">
          <div style="
            background: white;
            width: 50px;
            height: 50px;
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
          ">${getAgentEmoji(agentName)}</div>
          <div>
            <h2 style="margin: 0; color: white; font-size: 1.3em; font-weight: 600;">${agentName}</h2>
            <p style="margin: 0; color: rgba(255,255,255,0.8); font-size: 0.9em;">Port ${agentPort}</p>
          </div>
        </div>

        <!-- Universal Chat -->
        <div style="flex: 1; max-width: 500px; min-width: 300px;">
          <div style="position: relative;">
            <input
              type="text"
              id="dw-universal-chat-input"
              placeholder="💬 Chat with ${agentName} AI..."
              style="
                width: 100%;
                padding: 12px 50px 12px 15px;
                border: none;
                border-radius: 25px;
                font-size: 14px;
                background: rgba(255,255,255,0.95);
                box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                transition: all 0.3s ease;
              "
              onkeypress="if(event.key==='Enter') sendUniversalChat()"
            />
            <button
              onclick="sendUniversalChat()"
              style="
                position: absolute;
                right: 5px;
                top: 50%;
                transform: translateY(-50%);
                background: ${BRAND_COLORS.accent};
                color: white;
                border: none;
                border-radius: 50%;
                width: 38px;
                height: 38px;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                transition: all 0.2s ease;
                font-size: 18px;
              "
              onmouseover="this.style.background='${BRAND_COLORS.primary}'"
              onmouseout="this.style.background='${BRAND_COLORS.accent}'"
            >▶</button>
          </div>
        </div>

        <!-- Refresh Controls -->
        <div style="display: flex; align-items: center; gap: 15px;">
          <!-- Live Countdown -->
          <div style="
            background: rgba(255,255,255,0.2);
            padding: 10px 20px;
            border-radius: 25px;
            display: flex;
            align-items: center;
            gap: 10px;
            backdrop-filter: blur(10px);
          ">
            <span style="color: white; font-size: 0.9em;">Next refresh:</span>
            <span id="dw-countdown" style="
              color: white;
              font-weight: 700;
              font-size: 1.1em;
              min-width: 45px;
              font-family: monospace;
            ">--:--</span>
          </div>

          <!-- Manual Refresh Button -->
          <button
            onclick="manualRefreshPage()"
            id="dw-refresh-btn"
            style="
              background: white;
              color: ${BRAND_COLORS.primary};
              border: none;
              padding: 10px 20px;
              border-radius: 25px;
              cursor: pointer;
              font-weight: 600;
              display: flex;
              align-items: center;
              gap: 8px;
              transition: all 0.2s ease;
              box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            "
            onmouseover="this.style.transform='scale(1.05)'; this.style.boxShadow='0 4px 15px rgba(0,0,0,0.2)'"
            onmouseout="this.style.transform='scale(1)'; this.style.boxShadow='0 2px 10px rgba(0,0,0,0.1)'"
          >
            <span style="font-size: 18px;">🔄</span>
            <span>Refresh</span>
          </button>
        </div>
      </div>

      <!-- Chat Response Area (Hidden by default) -->
      <div id="dw-chat-response" style="
        display: none;
        margin-top: 15px;
        background: rgba(255,255,255,0.95);
        padding: 15px;
        border-radius: 12px;
        color: #333;
        max-height: 200px;
        overflow-y: auto;
        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
      "></div>
    </div>

    <script>
      // Live countdown timer (convert minutes to seconds)
      let countdownSeconds = ${refreshInterval} * 60;
      let countdownInterval;

      function updateCountdown() {
        const minutes = Math.floor(countdownSeconds / 60);
        const seconds = countdownSeconds % 60;
        document.getElementById('dw-countdown').textContent =
          minutes + ':' + (seconds < 10 ? '0' : '') + seconds;

        if (countdownSeconds <= 0) {
          countdownSeconds = ${refreshInterval} * 60;
          autoRefreshPage();
        } else {
          countdownSeconds--;
        }
      }

      function startCountdown() {
        countdownSeconds = ${refreshInterval} * 60;
        updateCountdown();
        if (countdownInterval) clearInterval(countdownInterval);
        countdownInterval = setInterval(updateCountdown, 1000);
      }

      function autoRefreshPage() {
        console.log('Auto-refreshing page data...');
        if (typeof updateDashboard === 'function') {
          updateDashboard();
        } else if (typeof refresh === 'function') {
          refresh();
        } else {
          location.reload();
        }
      }

      function manualRefreshPage() {
        const btn = document.getElementById('dw-refresh-btn');
        btn.style.opacity = '0.5';
        btn.disabled = true;

        console.log('Manual refresh triggered...');

        if (typeof updateDashboard === 'function') {
          updateDashboard().then(() => {
            btn.style.opacity = '1';
            btn.disabled = false;
            startCountdown();
          });
        } else if (typeof refresh === 'function') {
          refresh();
          setTimeout(() => {
            btn.style.opacity = '1';
            btn.disabled = false;
            startCountdown();
          }, 1000);
        } else {
          location.reload();
        }
      }

      async function sendUniversalChat() {
        const input = document.getElementById('dw-universal-chat-input');
        const message = input.value.trim();
        const responseDiv = document.getElementById('dw-chat-response');

        if (!message) return;

        // Show loading
        responseDiv.style.display = 'block';
        responseDiv.innerHTML = '<div style="text-align: center; color: #888;">💭 Thinking...</div>';

        try {
          const response = await fetch('/api/chat', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ message })
          });

          const data = await response.json();

          responseDiv.innerHTML = \`
            <div style="display: flex; gap: 10px;">
              <div style="font-size: 24px;">🤖</div>
              <div style="flex: 1;">
                <strong style="color: ${BRAND_COLORS.primary};">${agentName} AI:</strong>
                <p style="margin: 5px 0 0 0; line-height: 1.5;">\${data.response || data.message || 'No response'}</p>
              </div>
              <button onclick="document.getElementById('dw-chat-response').style.display='none'"
                style="background: none; border: none; cursor: pointer; font-size: 20px; color: #888;">×</button>
            </div>
          \`;

          input.value = '';
        } catch (error) {
          responseDiv.innerHTML = \`
            <div style="color: ${BRAND_COLORS.danger};">
              ⚠️ Error: Unable to connect to AI chat. \${error.message}
            </div>
          \`;
        }
      }

      // Start countdown on page load
      startCountdown();

      // Pause countdown when user is typing
      document.getElementById('dw-universal-chat-input').addEventListener('focus', () => {
        if (countdownInterval) clearInterval(countdownInterval);
      });

      document.getElementById('dw-universal-chat-input').addEventListener('blur', () => {
        if (!countdownInterval) startCountdown();
      });
    </script>
  `;
}

/**
 * Get emoji for agent based on name
 */
function getAgentEmoji(agentName) {
  const emojiMap = {
    'master-hub': '🏠',
    'task-orchestrator': '🎯',
    'purchasing-office': '🛒',
    'accounting': '💰',
    'marketing': '📢',
    'zendesk-chat': '💬',
    'digital-samples': '📦',
    'shopify-store': '🛍️',
    'log-monitor': '📋',
    'ui-manager': '🎨',
    'completed-tasks': '✅',
    'needs-attention': '⚠️',
    'new-client-signup': '👋',
    'todays-highlights': '⭐',
    'trend-research': '📊',
    'legal-team': '⚖️',
    'server-uptime': '🖥️'
  };

  for (const [key, emoji] of Object.entries(emojiMap)) {
    if (agentName.toLowerCase().includes(key)) return emoji;
  }
  return '🤖';
}

/**
 * Get theme-aware styles for injection into agent pages
 */
function getThemeStyles(themeName = 'modern-minimalist') {
  return getInlineThemeStyles(themeName);
}

/**
 * Get complete page wrapper with theme
 * Use this for new web viewers to automatically include theme
 */
function getThemedPageWrapper(agentName, agentPort, content, refreshInterval = 5) {
  const theme = getTheme('modern-minimalist');

  return `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>${agentName} - DW-Agents</title>
      ${getThemeStyles('modern-minimalist')}
    </head>
    <body>
      ${getUniversalHeader(agentName, agentPort, refreshInterval)}
      <div style="padding: ${theme.spacing.lg};">
        ${content}
      </div>
    </body>
    </html>
  `;
}

// Export for Node.js
if (typeof module !== 'undefined' && module.exports) {
  module.exports = {
    getUniversalHeader,
    getAgentEmoji,
    BRAND_COLORS,
    getThemeStyles,
    getThemedPageWrapper,
    DEFAULT_THEME
  };
}