← back to Designer Wallcoverings

DW-Agents/dw-agents/completed-tasks-agent.ts

536 lines

/**
 * DW-Agents: Completed Tasks Agent
 *
 * Shows all completed tasks from all agents in chronological order
 * Port: 9889
 */

import express, { Request, Response } from 'express';
import { requireGlobalAuth } from './shared-global-auth';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import { AgentMemory } from './shared-memory-system';

// Import universal UI components
import { getUniversalHeader, getThemedPageWrapper, getThemeStyles } from './shared-ui-components.js';

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


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

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

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


// Potential settlement violations - SORTED with most recent first
const potentialViolations = [
  {
    sku: 'AT7098',
    productName: 'Palm Paradise Tropical Wallpaper',
    url: 'https://designer-laboratory-sandbox.myshopify.com/products/palm-paradise',
    issue: 'Contains repeating palm fronds with directional variation + birds',
    partA: true,
    partB: 'Birds detected',
    timestamp: new Date('2025-11-07T18:30:00'),
    dateAdded: new Date('2025-11-05T10:00:00')
  },
  {
    sku: 'WP2547',
    productName: 'Tropical Breeze Collection',
    url: 'https://designer-laboratory-sandbox.myshopify.com/products/tropical-breeze',
    issue: 'Repeating leaf patterns with open spaces + butterflies',
    partA: true,
    partB: 'Butterflies detected',
    timestamp: new Date('2025-11-07T16:15:00'),
    dateAdded: new Date('2025-11-04T14:30:00')
  },
  {
    sku: 'GB9201',
    productName: 'Banana Leaf Botanical',
    url: 'https://designer-laboratory-sandbox.myshopify.com/products/banana-leaf',
    issue: 'Contains banana pods + repeating pattern with color variation',
    partA: true,
    partB: 'Banana pods detected',
    timestamp: new Date('2025-11-07T14:45:00'),
    dateAdded: new Date('2025-11-03T09:15:00')
  }
].sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());

// Sample completed tasks data
const completedTasks = [
  { agent: 'Legal Team', task: 'Reviewed 15 products for settlement compliance', timestamp: new Date('2025-11-05T19:30:00'), status: 'All clear' },
  { agent: 'Marketing', task: 'Published blog post: "Top Wallpaper Trends 2025"', timestamp: new Date('2025-11-05T18:45:00'), status: 'Live' },
  { agent: 'Digital Samples', task: 'Processed DIG-25 order and delivered files', timestamp: new Date('2025-11-05T17:20:00'), status: 'Delivered' },
  { agent: 'Purchasing Office', task: 'Ordered printer paper (5 reams) from Amazon Business', timestamp: new Date('2025-11-05T16:10:00'), status: 'Ordered' },
  { agent: 'Trend Research', task: 'Generated market intelligence report', timestamp: new Date('2025-11-05T15:30:00'), status: 'Complete' },
  { agent: 'Accounting', task: 'Generated P&L report for October 2025', timestamp: new Date('2025-11-05T14:00:00'), status: 'Complete' },
  { agent: 'Zendesk Chat', task: 'Resolved 8 customer inquiries', timestamp: new Date('2025-11-05T13:45:00'), status: 'Closed' },
  { agent: 'Server Uptime', task: 'Auto-restarted dw-master-hub', timestamp: new Date('2025-11-05T12:30:00'), status: 'Online' },
  { agent: 'Digital Samples', task: 'Processed DIG-24 order', timestamp: new Date('2025-11-05T11:15:00'), status: 'Delivered' },
  { agent: 'Legal Team', task: 'Updated settlement criteria documentation', timestamp: new Date('2025-11-05T10:00:00'), status: 'Updated' }
];

// Main dashboard
app.get('/', (req: Request, res: Response) => {
  // Prevent caching
  res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
  res.setHeader('Pragma', 'no-cache');
  res.setHeader('Expires', '0');

  res.send(`
<!DOCTYPE html>
<html>
<head>
  <title>Completed Tasks - 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: #ffffff;
      padding: 20px;
      min-height: 100vh;
    }
    .header {
      background: #000000;
      padding: 30px;
      border-radius: 0;
      margin-bottom: 20px;
      border: 4px solid #000;
    }
    .header h1 { color: #ffffff; font-size: 3em; margin-bottom: 10px; font-weight: 700; }
    .header .subtitle { color: #ffffff; font-size: 1.3em; font-weight: 500; }
    .timeline {
      background: white;
      border-radius: 0;
      padding: 40px;
      border: 3px solid #000;
    }
    .task-item {
      border-left: 8px solid #000000;
      padding: 25px;
      margin-bottom: 25px;
      background: #ffffff;
      border-radius: 0;
      transition: all 0.2s;
      border: 2px solid #000;
    }
    .task-item:hover {
      transform: translateX(8px);
      background: #f5f5f5;
    }
    .task-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
      border-bottom: 2px solid #000;
      padding-bottom: 10px;
    }
    .task-agent {
      font-weight: 700;
      color: #000000;
      font-size: 1.4em;
      text-transform: uppercase;
    }
    .task-time {
      color: #000;
      font-size: 1.1em;
      font-weight: 700;
    }
    .task-description {
      color: #000;
      margin-bottom: 12px;
      font-size: 1.2em;
      line-height: 1.6;
      font-weight: 500;
    }
    .task-status {
      display: inline-block;
      background: #000000;
      color: #ffffff;
      padding: 8px 16px;
      border-radius: 0;
      font-size: 1em;
      font-weight: 700;
      text-transform: uppercase;
    }
    .filters {
      background: white;
      padding: 25px;
      border-radius: 0;
      margin-bottom: 20px;
      border: 3px solid #000;
      display: flex;
      gap: 20px;
      align-items: center;
    }
    .filters select {
      padding: 12px 15px;
      border: 3px solid #000000;
      border-radius: 0;
      font-size: 1.1em;
      font-weight: 700;
      background: white;
      color: #000;
    }
    .filters label {
      font-weight: 700;
      color: #000;
      font-size: 1.2em;
      text-transform: uppercase;
    }
    .settlement-section {
      background: rgba(220, 20, 60, 0.15);
      border: 3px solid #dc143c;
      padding: 30px;
      border-radius: 15px;
      margin-bottom: 20px;
      box-shadow: 0 5px 20px rgba(220, 20, 60, 0.3);
    }
    .settlement-section h2 {
      color: #dc143c;
      margin-bottom: 20px;
      font-size: 1.8em;
    }
    .violation-rule {
      background: rgba(255, 0, 0, 0.2);
      border: 2px solid #ff0000;
      padding: 20px;
      border-radius: 8px;
      margin-bottom: 20px;
      font-size: 1.1em;
      font-weight: bold;
      color: #8b0000;
      text-align: center;
    }
    .criteria-box {
      background: rgba(0, 0, 0, 0.05);
      padding: 20px;
      border-radius: 8px;
      margin-bottom: 15px;
      border-left: 4px solid #dc143c;
    }
    .criteria-box h3 {
      color: #dc143c;
      margin-bottom: 15px;
      font-size: 1.2em;
    }
    .criteria-box ul {
      list-style: none;
      padding: 0;
    }
    .criteria-box li {
      padding: 8px 0;
      color: #333;
    }
    .requirement {
      background: rgba(220, 20, 60, 0.2);
      padding: 10px;
      border-radius: 6px;
      margin-top: 15px;
      font-weight: bold;
      color: #8b0000;
    }
    .criteria-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 20px;
      margin-bottom: 20px;
    }
    @media (max-width: 768px) {
      .criteria-grid {
        grid-template-columns: 1fr;
      }
    }
    .violations-section {
      background: rgba(255, 0, 0, 0.1);
      border: 4px solid #ff0000;
      padding: 30px;
      border-radius: 15px;
      margin-bottom: 25px;
      box-shadow: 0 8px 30px rgba(255, 0, 0, 0.4);
      animation: pulse-red 2s infinite;
    }
    @keyframes pulse-red {
      0%, 100% { box-shadow: 0 8px 30px rgba(255, 0, 0, 0.4); }
      50% { box-shadow: 0 8px 40px rgba(255, 0, 0, 0.6); }
    }
    .violations-section h2 {
      color: #ff0000;
      margin-bottom: 20px;
      font-size: 2em;
      font-weight: 800;
      text-align: center;
    }
    .violation-item {
      background: white;
      border-left: 6px solid #ff0000;
      padding: 20px;
      margin-bottom: 15px;
      border-radius: 8px;
      box-shadow: 0 3px 10px rgba(0,0,0,0.1);
    }
    .violation-item h3 {
      color: #ff0000;
      margin-bottom: 10px;
      font-size: 1.3em;
    }
    .violation-item .sku {
      background: #ff0000;
      color: white;
      padding: 4px 12px;
      border-radius: 4px;
      font-weight: bold;
      display: inline-block;
      margin-bottom: 10px;
    }
    .violation-item .issue {
      color: #333;
      margin: 10px 0;
      font-size: 1.1em;
    }
    .violation-item .details {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
      margin-top: 10px;
      padding-top: 10px;
      border-top: 1px solid #eee;
    }
    .violation-item .detail-item {
      font-size: 0.95em;
      color: #666;
    }
    .violation-item .detail-item strong {
      color: #ff0000;
    }
    .violation-item a {
      color: #0066cc;
      text-decoration: none;
      font-weight: 600;
    }
    .violation-item a:hover {
      text-decoration: underline;
    }
  </style>
</head>
<body>
  ${getUniversalHeader('Completed Tasks', PORT, 5)}

  <div class="header" style="margin-top: 20px;">
    <h1>✅ Completed Tasks</h1>
    <div class="subtitle">All completed tasks from DW-Agents in chronological order</div>
  </div>

  <div class="violations-section">
    <h2>🚨 POTENTIAL SETTLEMENT VIOLATIONS - IMMEDIATE ATTENTION REQUIRED</h2>
    ${potentialViolations.map(v => `
      <div class="violation-item">
        <span class="sku">${v.sku}</span>
        <h3>${v.productName}</h3>
        <div class="issue"><strong>⚠️ Issue:</strong> ${v.issue}</div>
        <div class="details" style="grid-template-columns: 1fr 1fr 1fr 1fr;">
          <div class="detail-item"><strong>Part A:</strong> ${v.partA ? '✓ Met' : '✗ Not met'}</div>
          <div class="detail-item"><strong>Part B:</strong> ${v.partB}</div>
          <div class="detail-item"><strong>SKU Added:</strong> ${v.dateAdded.toLocaleDateString()}</div>
          <div class="detail-item"><strong>Analyzed:</strong> ${v.timestamp.toLocaleDateString()}</div>
        </div>
        <div style="margin-top: 10px;">
          <a href="${v.url}" target="_blank">View Product →</a>
          <span style="float: right; color: #888; font-size: 0.9em;">Last checked: ${v.timestamp.toLocaleString()}</span>
        </div>
      </div>
    `).join('')}
  </div>

  <div class="settlement-section">
    <h2>🚨 Settlement Agreement - Quick Reference</h2>

    <div class="violation-rule">
      ⚠️ VIOLATION OCCURS WHEN: BOTH Part A (all conditions) AND Part B (any element) are present
    </div>

    <div class="criteria-grid">
      <div class="criteria-box">
        <h3>Part A: Prohibited Design Pattern</h3>
        <ul>
          <li>✓ Repeating patterns with directional variation amongst leaves/palm fronds</li>
          <li>✓ Open space between leaves</li>
          <li>✓ More than one ink color</li>
        </ul>
        <div class="requirement">ALL three conditions must be present</div>
      </div>

      <div class="criteria-box">
        <h3>Part B: Prohibited Specific Elements</h3>
        <ul>
          <li>🚫 Bananas or banana pods</li>
          <li>🚫 Grapes</li>
          <li>🚫 Birds</li>
          <li>🚫 Butterflies</li>
        </ul>
        <div class="requirement">ANY one of these elements</div>
      </div>
    </div>

    <div class="criteria-box" style="border-left-color: #4CAF50;">
      <h3 style="color: #4CAF50;">✅ Acceptable Tropical Design Elements</h3>
      <ul>
        <li>✓ Tree trunks</li>
        <li>✓ Clearly represented branches</li>
        <li>✓ Fruit or animal elements (EXCEPT prohibited ones)</li>
      </ul>
      <div class="requirement" style="background: rgba(76, 175, 80, 0.2); color: #2e7d32;">At least ONE must be present for tropical designs</div>
    </div>
  </div>

  <div class="filters">
    <label>Filter by Agent:</label>
    <select id="agentFilter" onchange="filterTasks()">
      <option value="all">All Agents</option>
      <option value="Legal Team">Legal Team</option>
      <option value="Marketing">Marketing</option>
      <option value="Digital Samples">Digital Samples</option>
      <option value="Purchasing Office">Purchasing Office</option>
      <option value="Trend Research">Trend Research</option>
      <option value="Accounting">Accounting</option>
      <option value="Zendesk Chat">Zendesk Chat</option>
      <option value="Server Uptime">Server Uptime</option>
    </select>
    <span style="margin-left: auto; color: #888;" id="lastUpdate">Last updated: --:--</span>
  </div>

  <div class="timeline" id="timeline">
    ${completedTasks.map(task => `
      <div class="task-item" data-agent="${task.agent}">
        <div class="task-header">
          <div class="task-agent">${task.agent}</div>
          <div class="task-time">${task.timestamp.toLocaleString()}</div>
        </div>
        <div class="task-description">${task.task}</div>
        <div class="task-status">${task.status}</div>
      </div>
    `).join('')}
  </div>

  <script>
    function filterTasks() {
      const filter = document.getElementById('agentFilter').value;
      const items = document.querySelectorAll('.task-item');

      items.forEach(item => {
        if (filter === 'all' || item.dataset.agent === filter) {
          item.style.display = 'block';
        } else {
          item.style.display = 'none';
        }
      });
    }

    function updateTimestamp() {
      document.getElementById('lastUpdate').textContent = 'Last updated: ' + new Date().toLocaleTimeString();
    }

    updateTimestamp();
  </script>
</body>
</html>
  `);
});


// API: Chat endpoint for universal header
app.post('/api/chat', 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;
    const systemPrompt = `You are the Completed Tasks AI assistant for Designer Wallcoverings. Track and display all finished tasks across all agents`;

    const Anthropic = require('@anthropic-ai/sdk').default;
    const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY || '' });

    const response = await anthropic.messages.create({
      model: 'claude-3-5-sonnet-20241022',
      max_tokens: 1024,
      system: systemPrompt,
      messages: [{ role: 'user', content: message }],
    });

    const assistantMessage = response.content[0].type === 'text' ? response.content[0].text : '';
    res.json({ response: assistantMessage, success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Start server
app.listen(PORT, '0.0.0.0', () => {
  console.log(`✅ Completed Tasks Agent running on port ${PORT}`);
  console.log(`Dashboard: http://45.61.58.125:${PORT}`);
});