← back to Designer Wallcoverings
DW-Agents/dw-agents/agent-todays-highlights/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
};
}