← back to Designer Wallcoverings

DW-Agents/dw-agents/agent-marketing/marketing-agent.ts.backup-1762750384

1507 lines

/**
 * DW-Agents: Marketing Agent
 *
 * AI-powered marketing content creation and management:
 * 1. View and manage Shopify blog posts
 * 2. Create compelling blog posts from new SKUs
 * 3. Generate social media content (Instagram, Facebook)
 * 4. Draft email campaigns (Constant Contact integration)
 * 5. Chat interface for content creation assistance
 *
 * Port: 9881
 */

import Anthropic from '@anthropic-ai/sdk';
import express from 'express';
import { requireAuth as ssoAuth, handleLogin, setSSOToken, SSO_TOKEN_NAME, SSO_TOKEN_VALUE } from './shared-auth';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import fetch from 'node-fetch';
import { AgentMemory } from './shared-memory-system';

const app = express();
const PORT = 9881;
// Initialize agent memory system
const agentMemory = new AgentMemory('marketing');
console.log('📚 Memory system initialized for marketing');


// Authentication
const AUTH_USERNAME = 'admin';
const AUTH_PASSWORD = '2025';

// Shopify configuration
const SHOPIFY_STORE = process.env.SHOPIFY_STORE_DOMAIN || 'designer-laboratory-sandbox.myshopify.com';
const SHOPIFY_TOKEN = process.env.SHOPIFY_ACCESS_TOKEN || 'shpat_REDACTED';
const API_VERSION = '2024-07';

// Constant Contact configuration (placeholder)
const CC_API_KEY = process.env.CONSTANT_CONTACT_API_KEY || '';
const CC_ACCESS_TOKEN = process.env.CONSTANT_CONTACT_ACCESS_TOKEN || '';

// Anthropic Claude
const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY || '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',
});

// Stats tracking
let stats = {
  totalConversations: 0,
  blogPostsCreated: 0,
  socialPostsGenerated: 0,
  emailCampaignsDrafted: 0,
  lastBlogPost: null as { title: string; sku: string; timestamp: Date } | null,
  recentBlogs: [] as Array<{
    id: string;
    title: string;
    createdAt: string;
    publishedAt: string | null;
    tags: string;
  }>,
  recentProducts: [] as Array<{
    id: string;
    title: string;
    sku: string;
    vendor: string;
    handle: string;
    imageUrl: string;
  }>
};

app.use(express.json());
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));

app.use(
  session({
    secret: 'dw-marketing-agent-2025',
    resave: false,
    saveUninitialized: true,
    rolling: true,
    cookie: {
      secure: false,
      httpOnly: true,
      maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
    },
  })
);

declare module 'express-session' {
  interface SessionData {
    authenticated: boolean;
    chatHistory: Array<{ role: 'user' | 'assistant', content: string }>;
  }
}

const requireAuth = (req: express.Request, res: express.Response, next: express.NextFunction) => {
  // TEMPORARY: Auth disabled for 4 hours
  return next();

  /* Original auth:

  if (req.session.authenticated) {
    next();
  } else {
    res.redirect('/login');
  }
  */
};

// Shopify API helper
async function shopifyGraphQL(query: string, variables?: any) {
  const response = await fetch(`https://${SHOPIFY_STORE}/admin/api/${API_VERSION}/graphql.json`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Shopify-Access-Token': SHOPIFY_TOKEN,
    },
    body: JSON.stringify({ query, variables }),
  });
  return response.json();
}

// Login page
app.get('/login', (req, res) => {
  res.send(`
<!DOCTYPE html>
<html>
<head>
  <title>Login - Marketing Agent</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
    }
    .login-container {
      background: white;
      padding: 40px;
      border-radius: 15px;
      box-shadow: 0 15px 35px rgba(0,0,0,0.2);
      max-width: 400px;
      width: 100%;
    }
    h1 { color: #f5576c; text-align: center; margin-bottom: 30px; }
    input {
      width: 100%;
      padding: 12px;
      margin: 10px 0;
      border: 2px solid #e0e0e0;
      border-radius: 8px;
    }
    button {
      width: 100%;
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      color: white;
      border: none;
      padding: 15px;
      border-radius: 8px;
      cursor: pointer;
      font-weight: 600;
      margin-top: 10px;
    }
    .error { color: red; text-align: center; margin-bottom: 15px; }
  </style>
</head>
<body>
  <div class="login-container">
    <h1>📢 Marketing Agent</h1>
    <p style="text-align: center; color: #666; margin-bottom: 20px;">Content Creation & Campaigns</p>
    ${req.query.error ? '<div class="error">Invalid credentials</div>' : ''}
    <form method="POST">
      <input type="text" name="username" placeholder="Username" required>
      <input type="password" name="password" placeholder="Password" required>
      <button type="submit">Login</button>
    </form>
  </div>
</body>
</html>
  `);
});

app.post('/login', (req, res) => {
  const { username, password } = req.body;
  if (username === AUTH_USERNAME && password === AUTH_PASSWORD) {
    req.session.authenticated = true;
    req.session.chatHistory = [];
    res.redirect('/');
  } else {
    res.redirect('/login?error=1');
  }
});

app.get('/logout', (req, res) => {
  req.session.destroy(() => res.redirect('/login'));
});

// Main dashboard
app.get('/', requireAuth, async (req, res) => {
  res.send(`
<!DOCTYPE html>
<html>
<head>
  <title>Marketing Agent - DW-Agents</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      padding: 20px;
      min-height: 100vh;
    }
    .container {
      max-width: 1600px;
      margin: 0 auto;
      display: grid;
      grid-template-columns: 400px 1fr;
      gap: 20px;
    }

    /* Mobile-responsive sidebar */
    .sidebar {
      display: flex;
      flex-direction: column;
      gap: 20px;
    }
    .sidebar-toggle {
      display: none;
      position: fixed;
      top: 20px;
      left: 20px;
      z-index: 1001;
      background: white;
      border: none;
      border-radius: 50%;
      width: 50px;
      height: 50px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.2);
      cursor: pointer;
      font-size: 1.5em;
      color: #f5576c;
      transition: transform 0.3s;
    }
    .sidebar-toggle:hover {
      transform: scale(1.1);
    }
    .sidebar-toggle:focus {
      outline: 3px solid #f5576c;
      outline-offset: 2px;
    }

    .header {
      background: white;
      padding: 30px;
      border-radius: 15px;
      box-shadow: 0 5px 20px rgba(0,0,0,0.1);
    }
    .header h1 { color: #d8194e; font-size: 2em; margin-bottom: 10px; }
    .header .subtitle { color: #333; font-size: 1.1em; }
    .stats-card {
      background: white;
      padding: 20px;
      border-radius: 15px;
      box-shadow: 0 5px 20px rgba(0,0,0,0.1);
    }
    .stats-card h3 { color: #555; font-size: 0.85em; text-transform: uppercase; margin-bottom: 8px; }
    .stats-card .value { font-size: 2em; font-weight: bold; color: #d8194e; }
    .main-content {
      display: flex;
      flex-direction: column;
      gap: 20px;
    }
    .tab-container {
      background: white;
      border-radius: 15px;
      box-shadow: 0 5px 20px rgba(0,0,0,0.1);
      overflow: hidden;
    }

    /* Scrollable tabs for mobile */
    .tabs {
      display: flex;
      background: #f8f9fa;
      border-bottom: 2px solid #e0e0e0;
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: thin;
    }
    .tabs::-webkit-scrollbar {
      height: 4px;
    }
    .tabs::-webkit-scrollbar-track {
      background: #f1f1f1;
    }
    .tabs::-webkit-scrollbar-thumb {
      background: #f5576c;
      border-radius: 4px;
    }
    .tab {
      flex: 1;
      min-width: 140px;
      padding: 15px;
      text-align: center;
      cursor: pointer;
      font-weight: 600;
      color: #333;
      transition: all 0.3s;
      white-space: nowrap;
    }
    .tab:hover {
      background: #f0f0f0;
    }
    .tab:focus {
      outline: 3px solid #f5576c;
      outline-offset: -3px;
      z-index: 1;
    }
    .tab.active {
      background: white;
      color: #d8194e;
      border-bottom: 3px solid #d8194e;
    }
    .tab-content {
      padding: 30px;
      display: none;
    }
    .tab-content.active {
      display: block;
    }

    /* Loading spinner */
    .spinner {
      display: inline-block;
      width: 20px;
      height: 20px;
      border: 3px solid rgba(255,255,255,0.3);
      border-radius: 50%;
      border-top-color: white;
      animation: spin 0.8s linear infinite;
    }
    @keyframes spin {
      to { transform: rotate(360deg); }
    }
    .loading-overlay {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background: rgba(0,0,0,0.5);
      display: none;
      justify-content: center;
      align-items: center;
      z-index: 9999;
    }
    .loading-overlay.active {
      display: flex;
    }
    .loading-content {
      background: white;
      padding: 30px 40px;
      border-radius: 15px;
      text-align: center;
      box-shadow: 0 10px 40px rgba(0,0,0,0.3);
    }
    .loading-spinner {
      width: 50px;
      height: 50px;
      border: 4px solid #f0f0f0;
      border-top-color: #f5576c;
      border-radius: 50%;
      animation: spin 0.8s linear infinite;
      margin: 0 auto 15px;
    }

    /* Toast notifications */
    .toast {
      position: fixed;
      top: 20px;
      right: 20px;
      background: white;
      padding: 16px 24px;
      border-radius: 10px;
      box-shadow: 0 8px 24px rgba(0,0,0,0.2);
      display: flex;
      align-items: center;
      gap: 12px;
      z-index: 10000;
      transform: translateX(400px);
      transition: transform 0.3s ease-out;
      max-width: 350px;
    }
    .toast.show {
      transform: translateX(0);
    }
    .toast.error {
      border-left: 4px solid #dc3545;
    }
    .toast.success {
      border-left: 4px solid #28a745;
    }
    .toast.info {
      border-left: 4px solid #17a2b8;
    }
    .toast-icon {
      font-size: 1.5em;
      flex-shrink: 0;
    }
    .toast-message {
      flex: 1;
      color: #333;
      line-height: 1.4;
    }
    .toast-close {
      background: none;
      border: none;
      color: #888;
      cursor: pointer;
      font-size: 1.2em;
      padding: 0;
      width: 24px;
      height: 24px;
      display: flex;
      align-items: center;
      justify-content: center;
      border-radius: 4px;
      flex-shrink: 0;
    }
    .toast-close:hover {
      background: #f0f0f0;
      color: #333;
    }
    .toast-close:focus {
      outline: 2px solid #f5576c;
    }

    .chat-container {
      height: 500px;
      display: flex;
      flex-direction: column;
    }
    .chat-messages {
      flex: 1;
      overflow-y: auto;
      padding: 20px;
      background: #f8f9fa;
      border-radius: 10px;
      margin-bottom: 20px;
    }
    .message {
      margin-bottom: 15px;
      display: flex;
      gap: 10px;
    }
    .message.user {
      flex-direction: row-reverse;
    }
    .message-avatar {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 1.2em;
      flex-shrink: 0;
    }
    .message.user .message-avatar {
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
    }
    .message.assistant .message-avatar {
      background: #e0e0e0;
    }
    .message-content {
      max-width: 70%;
      padding: 12px 16px;
      border-radius: 12px;
      line-height: 1.5;
    }
    .message.user .message-content {
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      color: white;
      border-bottom-right-radius: 4px;
    }
    .message.assistant .message-content {
      background: white;
      color: #1a1a1a;
      border-bottom-left-radius: 4px;
      box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    }
    .chat-input-container {
      display: flex;
      gap: 10px;
    }
    #chatInput {
      flex: 1;
      padding: 12px 16px;
      border: 2px solid #e0e0e0;
      border-radius: 25px;
      font-size: 1em;
      outline: none;
      color: #1a1a1a;
    }
    #chatInput:focus {
      border-color: #f5576c;
      box-shadow: 0 0 0 3px rgba(245, 87, 108, 0.1);
    }
    .send-btn {
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      color: white;
      border: none;
      padding: 12px 30px;
      border-radius: 25px;
      cursor: pointer;
      font-weight: 600;
      transition: all 0.2s;
    }
    .send-btn:hover:not(:disabled) {
      transform: translateY(-1px);
      box-shadow: 0 4px 12px rgba(245, 87, 108, 0.3);
    }
    .send-btn:focus {
      outline: 3px solid #f5576c;
      outline-offset: 2px;
    }
    .send-btn:active:not(:disabled) {
      transform: translateY(0);
    }
    .send-btn:disabled {
      opacity: 0.6;
      cursor: not-allowed;
    }
    .product-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
      gap: 15px;
      max-height: 400px;
      overflow-y: auto;
    }
    .product-card {
      border: 2px solid #e0e0e0;
      border-radius: 10px;
      padding: 15px;
      cursor: pointer;
      transition: all 0.3s;
    }
    .product-card:hover {
      border-color: #f5576c;
      transform: translateY(-2px);
      box-shadow: 0 5px 15px rgba(0,0,0,0.1);
    }
    .product-card:focus {
      outline: 3px solid #f5576c;
      outline-offset: 2px;
    }
    .product-card.selected {
      border-color: #f5576c;
      background: #fff5f7;
    }
    .product-image {
      width: 100%;
      height: 150px;
      object-fit: cover;
      border-radius: 8px;
      margin-bottom: 10px;
    }
    .product-title {
      font-weight: 600;
      font-size: 0.9em;
      margin-bottom: 5px;
      color: #1a1a1a;
    }
    .product-sku {
      font-size: 0.8em;
      color: #555;
    }
    .blog-list {
      max-height: 400px;
      overflow-y: auto;
    }
    .blog-item {
      padding: 15px;
      border-bottom: 1px solid #e0e0e0;
      cursor: pointer;
      transition: background 0.2s;
    }
    .blog-item:hover {
      background: #f8f9fa;
    }
    .blog-item:focus {
      outline: 2px solid #f5576c;
      outline-offset: -2px;
      background: #f8f9fa;
    }
    .blog-title {
      font-weight: 600;
      color: #1a1a1a;
      margin-bottom: 5px;
    }
    .blog-meta {
      font-size: 0.85em;
      color: #555;
    }
    .btn {
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      color: white;
      border: none;
      padding: 12px 24px;
      border-radius: 8px;
      cursor: pointer;
      font-weight: 600;
      margin-top: 15px;
      transition: all 0.2s;
    }
    .btn:hover:not(:disabled) {
      transform: translateY(-1px);
      box-shadow: 0 4px 12px rgba(245, 87, 108, 0.3);
    }
    .btn:focus {
      outline: 3px solid #d8194e;
      outline-offset: 2px;
    }
    .btn:active:not(:disabled) {
      transform: translateY(0);
    }
    .btn:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }
    .suggestions {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      margin-bottom: 15px;
    }
    .suggestion-chip {
      background: #f0f0f0;
      padding: 8px 15px;
      border-radius: 20px;
      font-size: 0.85em;
      cursor: pointer;
      transition: all 0.2s;
      color: #333;
    }
    .suggestion-chip:hover {
      background: #f5576c;
      color: white;
    }
    .suggestion-chip:focus {
      outline: 2px solid #f5576c;
      outline-offset: 2px;
    }

    /* Mobile responsive */
    @media (max-width: 1024px) {
      .container {
        grid-template-columns: 1fr;
      }
      .sidebar {
        position: fixed;
        left: 0;
        top: 0;
        bottom: 0;
        width: 300px;
        background: white;
        z-index: 1000;
        padding: 20px;
        overflow-y: auto;
        transform: translateX(-100%);
        transition: transform 0.3s ease-out;
        box-shadow: 2px 0 8px rgba(0,0,0,0.1);
      }
      .sidebar.open {
        transform: translateX(0);
      }
      .sidebar-toggle {
        display: flex;
        align-items: center;
        justify-content: center;
      }
      .sidebar-overlay {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: rgba(0,0,0,0.5);
        z-index: 999;
      }
      .sidebar-overlay.active {
        display: block;
      }
      .main-content {
        margin-top: 70px;
      }
      .tab {
        font-size: 0.9em;
        padding: 12px 10px;
      }
      .message-content {
        max-width: 85%;
      }
    }

    @media (max-width: 640px) {
      body {
        padding: 10px;
      }
      .tab-content {
        padding: 20px 15px;
      }
      .chat-container {
        height: 400px;
      }
      .product-grid {
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
        gap: 10px;
      }
      .toast {
        right: 10px;
        left: 10px;
        max-width: none;
      }
    }
  </style>
</head>
<body>
  <!-- Mobile sidebar toggle -->
  <button class="sidebar-toggle" onclick="toggleSidebar()" aria-label="Toggle menu">☰</button>
  <div class="sidebar-overlay" onclick="toggleSidebar()"></div>

  <!-- Loading overlay -->
  <div class="loading-overlay" id="loadingOverlay">
    <div class="loading-content">
      <div class="loading-spinner"></div>
      <div style="color: #333; font-weight: 600;">Processing...</div>
    </div>
  </div>

  <div class="container">
    <div class="sidebar" id="sidebar">
      <div class="header">
        <h1>📢 Marketing Agent</h1>
        <div class="subtitle">Content Creation & Campaigns</div>
        <div style="margin-top: 15px;">
          <a href="/logout" style="color: #f5576c; text-decoration: none;">Logout</a>
        </div>
      </div>

      <div class="stats-card">
        <h3>Blog Posts Created</h3>
        <div class="value" id="blogPostsCreated">0</div>
      </div>

      <div class="stats-card">
        <h3>Social Posts</h3>
        <div class="value" id="socialPostsGenerated">0</div>
      </div>

      <div class="stats-card">
        <h3>Email Campaigns</h3>
        <div class="value" id="emailCampaignsDrafted">0</div>
      </div>

      <div class="stats-card">
        <h3>Conversations</h3>
        <div class="value" id="totalConversations">0</div>
      </div>
    </div>

    <div class="main-content">
      <div class="tab-container">
        <div class="tabs">
          <div class="tab active" onclick="switchTab('chat')" tabindex="0" role="tab" aria-selected="true" onkeypress="if(event.key==='Enter') switchTab('chat')">💬 Chat Assistant</div>
          <div class="tab" onclick="switchTab('blogs')" tabindex="0" role="tab" aria-selected="false" onkeypress="if(event.key==='Enter') switchTab('blogs')">📝 Blog Posts</div>
          <div class="tab" onclick="switchTab('create')" tabindex="0" role="tab" aria-selected="false" onkeypress="if(event.key==='Enter') switchTab('create')">✨ Create from SKU</div>
          <div class="tab" onclick="switchTab('social')" tabindex="0" role="tab" aria-selected="false" onkeypress="if(event.key==='Enter') switchTab('social')">📱 Social Media</div>
        </div>

        <!-- Chat Tab -->
        <div class="tab-content active" id="chat-content">
          <h2 style="color: #f5576c; margin-bottom: 15px;">Chat with Marketing Agent</h2>
          <div class="suggestions">
            <div class="suggestion-chip" onclick="sendSuggestion('Show me recent blog posts')" tabindex="0" onkeypress="if(event.key==='Enter') sendSuggestion('Show me recent blog posts')">📝 Recent blogs</div>
            <div class="suggestion-chip" onclick="sendSuggestion('Create a blog post from a new product')" tabindex="0" onkeypress="if(event.key==='Enter') sendSuggestion('Create a blog post from a new product')">✨ Create blog</div>
            <div class="suggestion-chip" onclick="sendSuggestion('Generate Instagram post')" tabindex="0" onkeypress="if(event.key==='Enter') sendSuggestion('Generate Instagram post')">📸 Instagram post</div>
            <div class="suggestion-chip" onclick="sendSuggestion('Draft email campaign')" tabindex="0" onkeypress="if(event.key==='Enter') sendSuggestion('Draft email campaign')">📧 Email campaign</div>
          </div>
          <div class="chat-container">
            <div class="chat-messages" id="chatMessages">
              <div class="message assistant">
                <div class="message-avatar">🤖</div>
                <div class="message-content">
                  Hi! I'm your Marketing Agent. I can help you:<br><br>
                  • Create blog posts from new SKUs<br>
                  • View and manage Shopify blog posts<br>
                  • Generate social media content<br>
                  • Draft email campaigns<br>
                  • Write compelling product descriptions<br>
                  <br>
                  What would you like to create today?
                </div>
              </div>
            </div>
            <div class="chat-input-container">
              <input
                type="text"
                id="chatInput"
                placeholder="Ask me to create content, view blogs, or generate posts..."
                onkeypress="if(event.key === 'Enter') sendMessage()"
              >
              <button class="send-btn" onclick="sendMessage()" id="sendBtn">Send</button>
            </div>
          </div>
        </div>

        <!-- Blog Posts Tab -->
        <div class="tab-content" id="blogs-content">
          <h2 style="color: #f5576c; margin-bottom: 15px;">Recent Blog Posts from Shopify</h2>
          <button class="btn" onclick="loadBlogs()">🔄 Refresh Blog List</button>
          <div class="blog-list" id="blogList">
            <p style="color: #888; padding: 20px;">Click "Refresh Blog List" to load recent posts from Shopify...</p>
          </div>
        </div>

        <!-- Create from SKU Tab -->
        <div class="tab-content" id="create-content">
          <h2 style="color: #f5576c; margin-bottom: 15px;">Create Blog Post from Product</h2>
          <button class="btn" onclick="loadProducts()">🔄 Load Recent Products</button>
          <p style="color: #666; margin: 15px 0;">Select a product below to generate a blog post:</p>
          <div class="product-grid" id="productGrid">
            <p style="color: #888; padding: 20px; grid-column: 1/-1;">Click "Load Recent Products" to see available SKUs...</p>
          </div>
          <button class="btn" id="generateBlogBtn" style="display:none;" onclick="generateBlog()">✨ Generate Blog Post</button>
        </div>

        <!-- Social Media Tab -->
        <div class="tab-content" id="social-content">
          <h2 style="color: #f5576c; margin-bottom: 15px;">Social Media Content Generator</h2>
          <p style="color: #666; margin-bottom: 20px;">Generate social media posts for Instagram, Facebook, and more!</p>
          <button class="btn" onclick="generateSocial('instagram')">📸 Generate Instagram Post</button>
          <button class="btn" onclick="generateSocial('facebook')">👍 Generate Facebook Post</button>
          <button class="btn" onclick="generateSocial('pinterest')">📌 Generate Pinterest Description</button>
          <div id="socialOutput" style="margin-top: 20px; padding: 20px; background: #f8f9fa; border-radius: 10px; display: none;">
            <h3 style="color: #f5576c; margin-bottom: 10px;">Generated Content:</h3>
            <pre id="socialContent" style="white-space: pre-wrap; font-family: inherit; color: #333;"></pre>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    let selectedProduct = null;

    // Toast notification system
    function showToast(message, type = 'info') {
      const existingToast = document.querySelector('.toast');
      if (existingToast) {
        existingToast.remove();
      }

      const toast = document.createElement('div');
      toast.className = \`toast \${type}\`;

      const icons = {
        success: '✓',
        error: '✕',
        info: 'ℹ'
      };

      toast.innerHTML = \`
        <div class="toast-icon">\${icons[type] || icons.info}</div>
        <div class="toast-message">\${message}</div>
        <button class="toast-close" onclick="this.parentElement.remove()" aria-label="Close">×</button>
      \`;

      document.body.appendChild(toast);

      setTimeout(() => toast.classList.add('show'), 10);

      setTimeout(() => {
        toast.classList.remove('show');
        setTimeout(() => toast.remove(), 300);
      }, 5000);
    }

    // Loading overlay functions
    function showLoading() {
      document.getElementById('loadingOverlay').classList.add('active');
    }

    function hideLoading() {
      document.getElementById('loadingOverlay').classList.remove('active');
    }

    // Mobile sidebar toggle
    function toggleSidebar() {
      const sidebar = document.getElementById('sidebar');
      const overlay = document.querySelector('.sidebar-overlay');
      sidebar.classList.toggle('open');
      overlay.classList.toggle('active');
    }

    function switchTab(tab) {
      // Update tab states
      document.querySelectorAll('.tab').forEach(t => {
        t.classList.remove('active');
        t.setAttribute('aria-selected', 'false');
      });
      document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));

      // Activate selected tab
      event.target.classList.add('active');
      event.target.setAttribute('aria-selected', 'true');
      document.getElementById(tab + '-content').classList.add('active');

      // Close sidebar on mobile after tab switch
      if (window.innerWidth <= 1024) {
        const sidebar = document.getElementById('sidebar');
        const overlay = document.querySelector('.sidebar-overlay');
        if (sidebar.classList.contains('open')) {
          sidebar.classList.remove('open');
          overlay.classList.remove('active');
        }
      }
    }

    function updateStats() {
      fetch('/api/stats')
        .then(r => r.json())
        .then(data => {
          document.getElementById('blogPostsCreated').textContent = data.blogPostsCreated;
          document.getElementById('socialPostsGenerated').textContent = data.socialPostsGenerated;
          document.getElementById('emailCampaignsDrafted').textContent = data.emailCampaignsDrafted;
          document.getElementById('totalConversations').textContent = data.totalConversations;
        });
    }

    function addMessage(role, content) {
      const messagesDiv = document.getElementById('chatMessages');
      const messageDiv = document.createElement('div');
      messageDiv.className = \`message \${role}\`;

      const avatar = document.createElement('div');
      avatar.className = 'message-avatar';
      avatar.textContent = role === 'user' ? '👤' : '🤖';

      const contentDiv = document.createElement('div');
      contentDiv.className = 'message-content';
      contentDiv.innerHTML = content.replace(/\\n/g, '<br>');

      messageDiv.appendChild(avatar);
      messageDiv.appendChild(contentDiv);
      messagesDiv.appendChild(messageDiv);

      messagesDiv.scrollTop = messagesDiv.scrollHeight;
    }

    async function sendMessage() {
      const input = document.getElementById('chatInput');
      const sendBtn = document.getElementById('sendBtn');
      const message = input.value.trim();

      if (!message) return;

      addMessage('user', message);
      input.value = '';
      sendBtn.disabled = true;
      sendBtn.innerHTML = '<span class="spinner"></span>';
      showLoading();

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

        if (!response.ok) {
          throw new Error('Network response was not ok');
        }

        const data = await response.json();
        addMessage('assistant', data.response);
        updateStats();

      } catch (error) {
        console.error('Chat error:', error);
        addMessage('assistant', 'Sorry, I encountered an error. Please try again.');
        showToast('Failed to send message. Please try again.', 'error');
      } finally {
        hideLoading();
        sendBtn.disabled = false;
        sendBtn.textContent = 'Send';
      }
    }

    function sendSuggestion(text) {
      document.getElementById('chatInput').value = text;
      sendMessage();
    }

    async function loadBlogs() {
      const blogList = document.getElementById('blogList');
      blogList.innerHTML = '<p style="padding: 20px; color: #888;">Loading blog posts...</p>';
      showLoading();

      try {
        const response = await fetch('/api/shopify/blogs');

        if (!response.ok) {
          throw new Error('Failed to fetch blogs');
        }

        const data = await response.json();

        if (data.blogs && data.blogs.length > 0) {
          blogList.innerHTML = data.blogs.map(blog => \`
            <div class="blog-item" tabindex="0">
              <div class="blog-title">\${blog.title}</div>
              <div class="blog-meta">
                Created: \${new Date(blog.createdAt).toLocaleDateString()} |
                \${blog.publishedAt ? 'Published' : 'Draft'}
                \${blog.tags ? ' | Tags: ' + blog.tags : ''}
              </div>
            </div>
          \`).join('');
          showToast(\`Loaded \${data.blogs.length} blog posts\`, 'success');
        } else {
          blogList.innerHTML = '<p style="padding: 20px; color: #888;">No blog posts found.</p>';
          showToast('No blog posts found', 'info');
        }
      } catch (error) {
        console.error('Error loading blogs:', error);
        blogList.innerHTML = '<p style="padding: 20px; color: #dc3545;">Error loading blogs. Please try again.</p>';
        showToast('Failed to load blog posts', 'error');
      } finally {
        hideLoading();
      }
    }

    async function loadProducts() {
      const productGrid = document.getElementById('productGrid');
      productGrid.innerHTML = '<p style="padding: 20px; color: #888; grid-column: 1/-1;">Loading products...</p>';
      showLoading();

      try {
        const response = await fetch('/api/shopify/products');

        if (!response.ok) {
          throw new Error('Failed to fetch products');
        }

        const data = await response.json();

        if (data.products && data.products.length > 0) {
          productGrid.innerHTML = data.products.map(product => \`
            <div class="product-card" tabindex="0" onclick="selectProduct('\${product.id}', '\${product.sku}', '\${product.title}', '\${product.imageUrl}')" onkeypress="if(event.key==='Enter') selectProduct('\${product.id}', '\${product.sku}', '\${product.title}', '\${product.imageUrl}')">
              <img src="\${product.imageUrl || 'https://via.placeholder.com/200'}" class="product-image" alt="\${product.title}">
              <div class="product-title">\${product.title}</div>
              <div class="product-sku">SKU: \${product.sku}</div>
            </div>
          \`).join('');
          showToast(\`Loaded \${data.products.length} products\`, 'success');
        } else {
          productGrid.innerHTML = '<p style="padding: 20px; color: #888; grid-column: 1/-1;">No recent products found.</p>';
          showToast('No products found', 'info');
        }
      } catch (error) {
        console.error('Error loading products:', error);
        productGrid.innerHTML = '<p style="padding: 20px; color: #dc3545; grid-column: 1/-1;">Error loading products. Please try again.</p>';
        showToast('Failed to load products', 'error');
      } finally {
        hideLoading();
      }
    }

    function selectProduct(id, sku, title, imageUrl) {
      document.querySelectorAll('.product-card').forEach(card => card.classList.remove('selected'));
      event.currentTarget.classList.add('selected');
      selectedProduct = { id, sku, title, imageUrl };
      document.getElementById('generateBlogBtn').style.display = 'block';
    }

    async function generateBlog() {
      if (!selectedProduct) return;

      const btn = document.getElementById('generateBlogBtn');
      btn.disabled = true;
      btn.innerHTML = '<span class="spinner"></span> Generating...';
      showLoading();

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

        if (!response.ok) {
          throw new Error('Failed to generate blog');
        }

        const data = await response.json();

        if (data.success) {
          showToast(\`Blog post created: "\${data.blog.title}"\`, 'success');
          updateStats();
          setTimeout(() => {
            switchTab('blogs');
            loadBlogs();
          }, 500);
        } else {
          throw new Error(data.error || 'Unknown error');
        }

      } catch (error) {
        console.error('Error generating blog:', error);
        showToast('Failed to generate blog post. Please try again.', 'error');
      } finally {
        hideLoading();
        btn.disabled = false;
        btn.textContent = '✨ Generate Blog Post';
      }
    }

    async function generateSocial(platform) {
      const output = document.getElementById('socialOutput');
      const content = document.getElementById('socialContent');

      output.style.display = 'block';
      content.textContent = 'Generating ' + platform + ' content...';
      showLoading();

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

        if (!response.ok) {
          throw new Error('Failed to generate social content');
        }

        const data = await response.json();

        if (data.success) {
          content.textContent = data.content;
          updateStats();
          showToast(\`Generated \${platform} post successfully\`, 'success');
        } else {
          throw new Error('Generation failed');
        }

      } catch (error) {
        console.error('Error generating social content:', error);
        content.textContent = 'Error generating content. Please try again.';
        showToast('Failed to generate social content', 'error');
      } finally {
        hideLoading();
      }
    }

    // Update stats every 10 seconds
    setInterval(updateStats, 10000);
    updateStats();
  </script>
</body>
</html>
  `);
});

// API endpoints
app.get('/api/stats', requireAuth, (req, res) => {
  res.json(stats);
});

app.post('/api/chat', requireAuth, async (req, res) => {

  // Memory system commands
  if (message.toLowerCase().match(/^(remember|note|preference|learning):/i)) {
    const parts = message.split(':');
    const type = parts[0].toLowerCase();
    const content = parts.slice(1).join(':').trim();
    const memType = type === 'remember' ? 'note' : type as any;
    agentMemory.add(content, memType);
    return res.json({
      response: `✅ Memory saved: "${content}"`,
      success: true,
      memoryAdded: true
    });
  }

  if (message.toLowerCase().includes('show') && message.toLowerCase().includes('memor')) {
    const summary = agentMemory.getContextSummary();
    return res.json({ response: summary, success: true });
  }

  if (message.toLowerCase().includes('search memor')) {
    const query = message.split(/for|about/i)[1]?.trim() || '';
    const results = agentMemory.search(query);
    return res.json({
      response: results.length > 0
        ? 'Found memories:\n' + results.map(m => `- ${m.content}`).join('\n')
        : 'No matching memories found.',
      success: true
    });
  }

  if (message.toLowerCase().includes('forget') || message.toLowerCase().includes('delete memor')) {
    const query = message.split(/forget|delete/i)[1]?.trim() || '';
    const results = agentMemory.search(query);
    if (results.length > 0) {
      agentMemory.delete(results[0].id);
      return res.json({ response: `✅ Forgot: "${results[0].content}"`, success: true });
    }
    return res.json({ response: 'Nothing found to forget.', success: true });
  }

  try {
    const { message } = req.body;

    if (!message) {
      return res.status(400).json({ error: 'Message is required' });
    }

    stats.totalConversations++;

    if (!req.session.chatHistory) {
      req.session.chatHistory = [];
    }

    req.session.chatHistory.push({ role: 'user', content: message });

    const systemPrompt = `You are the Marketing Agent for Designer Wallcoverings.

Your responsibilities:
1. Create compelling blog posts from product SKUs
2. Generate social media content (Instagram, Facebook, Pinterest)
3. Draft email marketing campaigns
4. Write engaging product descriptions
5. Provide marketing strategy advice

You have access to:
- Shopify store with products and blog posts
- Recent product additions and SKUs
- Constant Contact for email campaigns

Current statistics:
- Blog posts created: ${stats.blogPostsCreated}
- Social posts generated: ${stats.socialPostsGenerated}
- Email campaigns: ${stats.emailCampaignsDrafted}

Recent blog posts:
${stats.recentBlogs.length > 0 ? stats.recentBlogs.slice(-3).map(blog =>
  '- "' + blog.title + '" (created ' + new Date(blog.createdAt).toLocaleDateString() + ')'
).join('\\n') : '- No recent blogs'}

${agentMemory.getContextSummary()}

When asked to create content:
1. Be creative and engaging
2. Use compelling headlines
3. Include SEO keywords naturally
4. Focus on benefits and style trends
5. Use designer wallcovering terminology
6. Reference stored memories and preferences when relevant

Be enthusiastic, creative, and marketing-focused in your responses.`;

    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 1024,
      system: systemPrompt,
      messages: req.session.chatHistory
    });

    const assistantMessage = response.content[0].type === 'text'
      ? response.content[0].text
      : 'I apologize, but I encountered an error.';

    req.session.chatHistory.push({ role: 'assistant', content: assistantMessage });

    if (req.session.chatHistory.length > 20) {
      req.session.chatHistory = req.session.chatHistory.slice(-20);
    }

    res.json({ response: assistantMessage });

  } catch (error) {
    console.error('Chat error:', error);
    res.status(500).json({
      error: 'Failed to process message',
      response: 'I apologize, but I encountered an error. Please try again.'
    });
  }
});

// Shopify blog posts
app.get('/api/shopify/blogs', requireAuth, async (req, res) => {
  try {
    const query = `
      query {
        articles(first: 10, sortKey: CREATED_AT, reverse: true) {
          edges {
            node {
              id
              title
              createdAt
              publishedAt
              tags
              handle
            }
          }
        }
      }
    `;

    const data = await shopifyGraphQL(query);

    if (data.data && data.data.articles) {
      const blogs = data.data.articles.edges.map((edge: any) => ({
        id: edge.node.id,
        title: edge.node.title,
        createdAt: edge.node.createdAt,
        publishedAt: edge.node.publishedAt,
        tags: edge.node.tags.join(', '),
        handle: edge.node.handle
      }));

      stats.recentBlogs = blogs;
      res.json({ blogs });
    } else {
      res.json({ blogs: [] });
    }

  } catch (error) {
    console.error('Error fetching blogs:', error);
    res.status(500).json({ error: 'Failed to fetch blogs' });
  }
});

// Shopify products
app.get('/api/shopify/products', requireAuth, async (req, res) => {
  try {
    const query = `
      query {
        products(first: 20, sortKey: CREATED_AT, reverse: true) {
          edges {
            node {
              id
              title
              handle
              vendor
              variants(first: 1) {
                edges {
                  node {
                    sku
                  }
                }
              }
              featuredImage {
                url
              }
            }
          }
        }
      }
    `;

    const data = await shopifyGraphQL(query);

    if (data.data && data.data.products) {
      const products = data.data.products.edges.map((edge: any) => ({
        id: edge.node.id,
        title: edge.node.title,
        handle: edge.node.handle,
        vendor: edge.node.vendor,
        sku: edge.node.variants.edges[0]?.node.sku || 'N/A',
        imageUrl: edge.node.featuredImage?.url || ''
      }));

      stats.recentProducts = products;
      res.json({ products });
    } else {
      res.json({ products: [] });
    }

  } catch (error) {
    console.error('Error fetching products:', error);
    res.status(500).json({ error: 'Failed to fetch products' });
  }
});

// Generate blog post from product
app.post('/api/generate/blog', requireAuth, async (req, res) => {
  try {
    const { id, sku, title, imageUrl } = req.body;

    // Use Claude to generate blog content
    const prompt = `Create a compelling blog post for this wallcovering product:

Product Title: ${title}
SKU: ${sku}

Write an engaging blog post (300-500 words) that includes:
1. An attention-grabbing headline
2. Introduction highlighting the product's unique features
3. Design trends and styling suggestions
4. Room application ideas
5. Call-to-action

Make it SEO-friendly and engaging for interior designers and homeowners.`;

    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 2048,
      messages: [{ role: 'user', content: prompt }]
    });

    const blogContent = response.content[0].type === 'text' ? response.content[0].text : '';

    // Extract title (first line) and body
    const lines = blogContent.split('\n');
    const blogTitle = lines[0].replace(/^#+ /, '').trim();
    const blogBody = lines.slice(1).join('\n').trim();

    stats.blogPostsCreated++;
    stats.lastBlogPost = {
      title: blogTitle,
      sku,
      timestamp: new Date()
    };

    // In production, would create blog post via Shopify API
    res.json({
      success: true,
      blog: {
        title: blogTitle,
        content: blogBody,
        sku,
        productId: id
      }
    });

  } catch (error) {
    console.error('Error generating blog:', error);
    res.status(500).json({ error: 'Failed to generate blog post' });
  }
});

// Generate social media content
app.post('/api/generate/social', requireAuth, async (req, res) => {
  try {
    const { platform } = req.body;

    const prompts: Record<string, string> = {
      instagram: 'Write an engaging Instagram caption (150 chars) for a new designer wallcovering collection. Include emojis and 5 relevant hashtags.',
      facebook: 'Write a Facebook post (200 chars) announcing a new wallcovering line. Make it informative and include a call-to-action.',
      pinterest: 'Write a Pinterest pin description (300 chars) for wallcovering design inspiration. Focus on style and room transformation.'
    };

    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 512,
      messages: [{ role: 'user', content: prompts[platform] || prompts.instagram }]
    });

    const content = response.content[0].type === 'text' ? response.content[0].text : '';

    stats.socialPostsGenerated++;

    res.json({
      success: true,
      platform,
      content
    });

  } catch (error) {
    console.error('Error generating social content:', error);
    res.status(500).json({ error: 'Failed to generate social content' });
  }
});

app.listen(PORT, '0.0.0.0', () => {
  console.log('');
  console.log('📢 Marketing Agent');
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
  console.log('🌍 External: http://45.61.58.125:' + PORT);
  console.log('🏠 Local: http://localhost:' + PORT);
  console.log('');
  console.log('💬 Chat Mode: Active');
  console.log('📝 Shopify Blog Integration: Ready');
  console.log('✨ AI Content Generation: Active');
  console.log('📱 Social Media Tools: Ready');
  console.log('📧 Email Campaigns: Ready');
  console.log('');
  console.log('✅ Marketing Agent ready for content creation...');
});