← back to Designer Wallcoverings

DW-Agents/dw-agents/agent-new-client-signup/new-client-signup-agent-v1.0.ts

437 lines

/**
 * DW-Agents: New Client Signup Monitor
 *
 * Monitors #new-client-signup Slack channel for customer signups
 * Auto-refreshes every 5 seconds
 * Port: 9890
 */

import express, { Request, Response } 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 { WebClient } from '@slack/web-api';
import { fetchRecentSignups, getSignupStats, CustomerSignup } from './shopify-customers';

const app = express();
const PORT = 9890;

// Slack configuration
const SLACK_TOKEN = process.env.SLACK_BOT_TOKEN || 'xoxb-your-token';
const SLACK_CHANNEL = 'new-client-signup';
const slack = new WebClient(SLACK_TOKEN);

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

declare module 'express-session' {
  interface SessionData {
    authenticated: boolean;
  }
}

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

const requireAuth = (req: Request, res: Response, next: any) => {
  if (req.session.authenticated) {
    return next();
  }
  if (req.cookies && req.cookies[SSO_TOKEN_NAME] === SSO_TOKEN_VALUE) {
    req.session.authenticated = true;
    return next();
  }
  res.redirect('/login');
};

// Login page
app.get('/login', (req: Request, res: Response) => {
  res.send(`
<!DOCTYPE html>
<html>
<head>
  <title>New Client Signups - Login</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, #7C3AED 0%, #A78BFA 100%);
      display: flex;
      align-items: center;
      justify-content: 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: #7C3AED; 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, #7C3AED 0%, #A78BFA 100%);
      color: white;
      border: none;
      padding: 15px;
      border-radius: 8px;
      cursor: pointer;
      font-weight: 600;
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <div class="login-container">
    <h1>🎯 New Client Signups</h1>
    <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: Request, res: Response) => {
  const { username, password } = req.body;
  if (username === 'admin' && password === '2025') {
    req.session.authenticated = true;
    setSSOToken(res);
    res.redirect('/');
  } else {
    res.redirect('/login');
  }
});

// Main dashboard
app.get('/', requireAuth, (req: Request, res: Response) => {
  res.send(`
<!DOCTYPE html>
<html>
<head>
  <title>New Client Signups - 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, #7C3AED 0%, #A78BFA 100%);
      padding: 20px;
      min-height: 100vh;
    }
    .header {
      background: white;
      padding: 30px;
      border-radius: 15px;
      margin-bottom: 20px;
      box-shadow: 0 5px 20px rgba(0,0,0,0.1);
    }
    .header h1 { color: #7C3AED; font-size: 2.5em; margin-bottom: 10px; }
    .header .subtitle { color: #666; font-size: 1.1em; }
    .stats-bar {
      background: white;
      padding: 20px 30px;
      border-radius: 15px;
      margin-bottom: 20px;
      box-shadow: 0 5px 20px rgba(0,0,0,0.1);
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      gap: 20px;
    }
    .stat-item {
      text-align: center;
    }
    .stat-number {
      font-size: 2.5em;
      font-weight: bold;
      color: #7C3AED;
    }
    .stat-label {
      color: #666;
      font-size: 0.9em;
      margin-top: 5px;
    }
    .refresh-indicator {
      background: white;
      padding: 15px 30px;
      border-radius: 15px;
      margin-bottom: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      box-shadow: 0 5px 20px rgba(0,0,0,0.1);
    }
    .refresh-indicator .pulse {
      width: 12px;
      height: 12px;
      background: #10B981;
      border-radius: 50%;
      animation: pulse 2s infinite;
      margin-right: 10px;
    }
    @keyframes pulse {
      0%, 100% { opacity: 1; transform: scale(1); }
      50% { opacity: 0.5; transform: scale(1.2); }
    }
    .signups-container {
      background: white;
      padding: 25px;
      border-radius: 15px;
      box-shadow: 0 5px 20px rgba(0,0,0,0.1);
      overflow-x: auto;
    }
    .signups-container h2 {
      color: #7C3AED;
      margin-bottom: 20px;
      font-size: 1.5em;
    }
    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 0.9em;
    }
    thead {
      background: #7C3AED;
      color: white;
      position: sticky;
      top: 0;
      z-index: 10;
    }
    th {
      padding: 12px 8px;
      text-align: left;
      font-weight: 600;
      border-right: 1px solid rgba(255,255,255,0.2);
    }
    th:last-child {
      border-right: none;
    }
    td {
      padding: 10px 8px;
      border-bottom: 1px solid #e0e0e0;
      vertical-align: top;
    }
    tr:hover {
      background: #f8f9fa;
    }
    tr.new {
      animation: highlightRow 2s ease-out;
    }
    @keyframes highlightRow {
      from { background: #FEF3C7; }
      to { background: white; }
    }
    .trade-badge {
      background: #10B981;
      color: white;
      padding: 2px 8px;
      border-radius: 8px;
      font-size: 0.8em;
      font-weight: 600;
    }
    .shopify-link {
      display: inline-block;
      background: #7C3AED;
      color: white;
      padding: 6px 12px;
      border-radius: 6px;
      text-decoration: none;
      font-weight: 600;
      font-size: 0.85em;
      transition: all 0.2s;
    }
    .shopify-link:hover {
      background: #6D28D9;
      transform: translateY(-1px);
      box-shadow: 0 2px 8px rgba(124, 58, 237, 0.3);
    }
    .no-signups {
      text-align: center;
      color: #888;
      padding: 40px;
      font-size: 1.1em;
    }
  </style>
</head>
<body>
  <div class="header">
    <h1>🎯 New Client Signups</h1>
    <div class="subtitle">Real-time Shopify customer data • Auto-refreshing every 5 seconds</div>
    <div style="margin-top: 10px; padding: 10px; background: #10B981; color: white; border-radius: 8px; text-align: center; font-weight: 600;">
      ✅ LIVE SHOPIFY DATA - NO MOCK DATA
    </div>
    <div style="margin-top: 15px; text-align: center;">
      <a href="https://admin.shopify.com/store/designer-laboratory-sandbox/customers" target="_blank" style="display: inline-block; padding: 12px 24px; background: #7C3AED; color: white; text-decoration: none; border-radius: 8px; font-weight: 600; box-shadow: 0 3px 10px rgba(124, 58, 237, 0.3); transition: all 0.2s;">
        🛍️ View All Clients in Shopify
      </a>
    </div>
  </div>

  <div class="stats-bar">
    <div class="stat-item">
      <div class="stat-number" id="todayCount">0</div>
      <div class="stat-label">Today</div>
    </div>
    <div class="stat-item">
      <div class="stat-number" id="thisWeekCount">0</div>
      <div class="stat-label">This Week</div>
    </div>
    <div class="stat-item">
      <div class="stat-number" id="thisMonthCount">0</div>
      <div class="stat-label">This Month</div>
    </div>
  </div>

  <div class="refresh-indicator">
    <div style="display: flex; align-items: center;">
      <div class="pulse"></div>
      <span style="color: #888;" id="lastUpdate">Auto-refreshing every 5 seconds...</span>
    </div>
    <span style="color: #10B981; font-weight: 600;" id="nextRefresh">Next: 5s</span>
  </div>

  <div class="signups-container">
    <h2>Recent Signups</h2>
    <div id="signupsList" class="no-signups">Loading signups...</div>
  </div>

  <script>
    let previousSignups = [];
    let countdown = 5;

    async function refreshSignups() {
      try {
        const data = await fetch('/api/signups').then(r => r.json());

        // Update stats
        document.getElementById('todayCount').textContent = data.stats.today;
        document.getElementById('thisWeekCount').textContent = data.stats.thisWeek;
        document.getElementById('thisMonthCount').textContent = data.stats.thisMonth;

        // Update signups list
        const signupsList = document.getElementById('signupsList');
        if (data.signups.length > 0) {
          const tableHTML = \`
            <table>
              <thead>
                <tr>
                  <th>Date</th>
                  <th>Name</th>
                  <th>Email</th>
                  <th>Company</th>
                  <th>Location</th>
                  <th>Orders</th>
                  <th>Spent</th>
                  <th>Type</th>
                  <th>Shopify</th>
                </tr>
              </thead>
              <tbody>
                \${data.signups.map(signup => {
                  const isNew = !previousSignups.some(s => s.timestamp === signup.timestamp);
                  const location = (signup.location || '').split(',');
                  const city = location[0] || 'N/A';
                  const state = location[1] || '';
                  const isTrade = (signup.tags || '').includes('trade') || (signup.tags || '').includes('Interior Designer') || (signup.tags || '').includes('Contractor') || (signup.tags || '').includes('Architect');

                  return \`
                    <tr class="\${isNew ? 'new' : ''}">
                      <td>\${new Date(signup.timestamp).toLocaleDateString()}</td>
                      <td><strong>\${signup.customerName || 'N/A'}</strong></td>
                      <td>\${signup.email || 'N/A'}</td>
                      <td>\${signup.company || 'Individual'}</td>
                      <td>\${city}\${state ? ', ' + state : ''}</td>
                      <td>\${signup.ordersCount || 0}</td>
                      <td>$\${signup.totalSpent || '0'}</td>
                      <td>\${isTrade ? '<span class="trade-badge">Trade</span>' : 'Customer'}</td>
                      <td>\${signup.id ? '<a href="https://admin.shopify.com/store/designer-laboratory-sandbox/customers/' + signup.id + '" target="_blank" class="shopify-link">View Client</a>' : 'N/A'}</td>
                    </tr>
                  \`;
                }).join('')}
              </tbody>
            </table>
          \`;

          signupsList.innerHTML = tableHTML;
          previousSignups = data.signups;
        } else {
          signupsList.innerHTML = '<div class="no-signups">No signups yet today</div>';
        }

        document.getElementById('lastUpdate').textContent = 'Last updated: ' + new Date().toLocaleTimeString();
        countdown = 5;
      } catch (error) {
        console.error('Error fetching signups:', error);
      }
    }

    // Countdown timer
    setInterval(() => {
      countdown--;
      if (countdown <= 0) {
        countdown = 5;
      }
      document.getElementById('nextRefresh').textContent = 'Next: ' + countdown + 's';
    }, 1000);

    // Auto-refresh every 5 seconds
    setInterval(refreshSignups, 5000);
    refreshSignups();
  </script>
</body>
</html>
  `);
});

// API endpoint to get signups - REAL SHOPIFY DATA
app.get('/api/signups', requireAuth, async (req: Request, res: Response) => {
  try {
    console.log('📊 Fetching real Shopify customer signups...');

    // Fetch real signups from Shopify - last month of customers
    const signups = await fetchRecentSignups(200);
    const stats = getSignupStats(signups);

    console.log(`✅ Returning ${signups.length} real signups from Shopify`);
    console.log(`   Today: ${stats.today}, Week: ${stats.thisWeek}, Month: ${stats.thisMonth}`);

    res.json({
      signups: signups,
      stats,
      generatedAt: new Date(),
      dataSource: 'Shopify API (REAL DATA)'
    });
  } catch (error) {
    console.error('❌ Error fetching signups:', error);
    res.status(500).json({ error: 'Failed to fetch signups' });
  }
});

app.listen(PORT, '0.0.0.0', () => {
  console.log(`🎯 New Client Signup Monitor running on port ${PORT}`);
  console.log(`Dashboard: http://45.61.58.125:${PORT}`);
});