<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Customer Details</title>
<style>
/* Inlined from shared/styles.css */
:root{color-scheme:light dark;--spacing-xs:4px;--spacing-sm:8px;--spacing-md:16px;--spacing-lg:24px;--spacing-xl:32px;--radius-sm:6px;--radius-md:8px;--radius-lg:12px;--accent-color:#0066cc;--color-bg-primary:#ffffff;--color-bg-secondary:#f7f7f8;--color-bg-tertiary:#ececf1;--color-border:#d9d9e3;--color-border-light:#ececf1;--color-text-primary:#2d333a;--color-text-secondary:#6e6e80;--color-text-tertiary:#9b9ba5;--color-accent:#0066cc;--color-accent-hover:#0052a3;--color-success:#10a37f;--color-warning:#f59e0b;--color-danger:#ef4444}@media (prefers-color-scheme:dark){:root{--color-bg-primary:#212121;--color-bg-secondary:#2f2f2f;--color-bg-tertiary:#3f3f3f;--color-border:#4d4d4d;--color-border-light:#3f3f3f;--color-text-primary:#ececec;--color-text-secondary:#c5c5d2;--color-text-tertiary:#9b9ba5;--color-accent:#3b82f6;--color-accent-hover:#2563eb;--color-success:#10a37f;--color-warning:#f59e0b;--color-danger:#ef4444}}*{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:14px;line-height:1.6;color:var(--color-text-primary);background:var(--color-bg-primary);padding:var(--spacing-md)}.customer-card-avatar{width:40px;height:40px;border-radius:50%;background:var(--color-bg-tertiary);display:flex;align-items:center;justify-content:center;font-weight:600;color:var(--color-text-secondary);font-size:16px;flex-shrink:0}.customer-card-icon{width:16px;height:16px;opacity:0.6}.detail-container{max-width:800px;margin:0 auto}.detail-header{margin-bottom:var(--spacing-lg);padding-bottom:var(--spacing-md);border-bottom:1px solid var(--color-border-light)}.detail-title{font-size:24px;font-weight:600;color:var(--color-text-primary);margin-bottom:var(--spacing-xs)}.detail-subtitle{font-size:14px;color:var(--color-text-secondary)}.detail-section{margin-bottom:var(--spacing-lg)}.detail-section-title{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;color:var(--color-text-tertiary);margin-bottom:var(--spacing-sm)}.detail-section-content{background:var(--color-bg-secondary);border-radius:var(--radius-md);padding:var(--spacing-md)}.detail-field{display:flex;justify-content:space-between;align-items:baseline;padding:var(--spacing-sm) 0;border-bottom:1px solid var(--color-border-light)}.detail-field:last-child{border-bottom:none}.detail-field-label{font-size:13px;color:var(--color-text-secondary);font-weight:500}.detail-field-value{font-size:14px;color:var(--color-text-primary);text-align:right}.empty-state{text-align:center;padding:var(--spacing-xl) var(--spacing-md);color:var(--color-text-secondary)}.empty-state-icon{font-size:48px;margin-bottom:var(--spacing-sm);opacity:0.3}.empty-state-title{font-size:16px;font-weight:600;color:var(--color-text-primary);margin-bottom:var(--spacing-xs)}.empty-state-message{font-size:14px;color:var(--color-text-secondary)}a{color:var(--color-accent);text-decoration:none;transition:color 0.15s ease}a:hover{color:var(--color-accent-hover);text-decoration:underline}.text-tertiary{color:var(--color-text-tertiary)}.mb-md{margin-bottom:var(--spacing-md)}
</style>
</head>
<body>
<div class="detail-container">
<div id="customer-detail"></div>
</div>
<script>
const SET_GLOBALS_EVENT_TYPE = 'openai:set_globals';
function getInitials(name) {
if (!name) return '?';
const parts = name.trim().split(' ');
if (parts.length === 1) return parts[0].charAt(0).toUpperCase();
return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
}
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = String(text);
return div.innerHTML;
}
function formatAddress(address) {
if (!address) return '<span class="text-tertiary">No address provided</span>';
const parts = [];
if (address.line1 || address.address1) parts.push(escapeHtml(address.line1 || address.address1));
if (address.line2 || address.address2) parts.push(escapeHtml(address.line2 || address.address2));
const cityLine = [];
if (address.city) cityLine.push(escapeHtml(address.city));
if (address.state) cityLine.push(escapeHtml(address.state));
if (address.postalCode) cityLine.push(escapeHtml(address.postalCode));
if (cityLine.length > 0) parts.push(cityLine.join(', '));
if (address.country) parts.push(escapeHtml(address.country));
return parts.length > 0
? parts.map(p => `<div>${p}</div>`).join('')
: '<span class="text-tertiary">No address provided</span>';
}
function addressesEqual(addr1, addr2) {
if (!addr1 || !addr2) return false;
return JSON.stringify(addr1) === JSON.stringify(addr2);
}
function renderContactSection(customer) {
const mainContact = customer.mainContact || {};
const email = mainContact.email || customer.email;
const phone = mainContact.phone || customer.phone;
const contactName = mainContact.name;
const position = mainContact.position;
if (!email && !phone && !contactName && !position) {
return '';
}
return `
<div class="detail-section">
<div class="detail-section-title">Contact Information</div>
<div class="detail-section-content">
${contactName ? `
<div class="detail-field">
<span class="detail-field-label">Contact Name</span>
<span class="detail-field-value">${escapeHtml(contactName)}</span>
</div>
` : ''}
${position ? `
<div class="detail-field">
<span class="detail-field-label">Position</span>
<span class="detail-field-value">${escapeHtml(position)}</span>
</div>
` : ''}
${email ? `
<div class="detail-field">
<span class="detail-field-label">
<svg class="customer-card-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" style="display: inline-block; vertical-align: middle; margin-right: 4px;">
<path d="M2 4l6 4 6-4M2 4v8h12V4M2 4h12" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Email
</span>
<span class="detail-field-value">
<a href="mailto:${escapeHtml(email)}">${escapeHtml(email)}</a>
</span>
</div>
` : ''}
${phone ? `
<div class="detail-field">
<span class="detail-field-label">
<svg class="customer-card-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" style="display: inline-block; vertical-align: middle; margin-right: 4px;">
<path d="M3 2h3l1 4-2 2c1 2 3 4 5 5l2-2 4 1v3a1 1 0 01-1 1C7 16 0 9 0 3a1 1 0 011-1z" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Phone
</span>
<span class="detail-field-value">${escapeHtml(phone)}</span>
</div>
` : ''}
</div>
</div>
`;
}
function renderAddressesSection(customer) {
const physicalAddress = customer.physicalAddress || customer.address;
const postalAddress = customer.postalAddress;
if (!physicalAddress && !postalAddress) {
return '';
}
let html = '<div class="detail-section"><div class="detail-section-title">Addresses</div>';
if (physicalAddress) {
html += `
<div class="detail-section-content mb-md">
<div class="detail-field" style="border-bottom: none; padding-bottom: 0;">
<span class="detail-field-label">
<svg class="customer-card-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" style="display: inline-block; vertical-align: middle; margin-right: 4px;">
<path d="M8 14s6-4 6-8a6 6 0 00-12 0c0 4 6 8 6 8z" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="8" cy="6" r="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Physical Address
</span>
</div>
<div style="padding-top: var(--spacing-sm); color: var(--color-text-primary);">
${formatAddress(physicalAddress)}
</div>
</div>
`;
}
if (postalAddress && !addressesEqual(physicalAddress, postalAddress)) {
html += `
<div class="detail-section-content">
<div class="detail-field" style="border-bottom: none; padding-bottom: 0;">
<span class="detail-field-label">
<svg class="customer-card-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" style="display: inline-block; vertical-align: middle; margin-right: 4px;">
<path d="M2 2h12v10H2z M2 4h12" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Postal Address
</span>
</div>
<div style="padding-top: var(--spacing-sm); color: var(--color-text-primary);">
${formatAddress(postalAddress)}
</div>
</div>
`;
}
html += '</div>';
return html;
}
function render() {
// Access toolOutput from window.openai
const toolOutput = window.openai?.toolOutput;
if (!toolOutput) {
console.log('[Fergus MCP] No toolOutput available yet');
return;
}
const customer = toolOutput.customer;
if (!customer) {
console.log('[Fergus MCP] No customer in toolOutput');
return;
}
console.log('[Fergus MCP] Rendering customer:', customer.id);
const container = document.getElementById('customer-detail');
if (!customer.id) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">❌</div>
<div class="empty-state-title">Customer not found</div>
<div class="empty-state-message">The requested customer could not be loaded</div>
</div>
`;
return;
}
const customerName = customer.name || customer.customerFullName || 'Unnamed Customer';
const initials = getInitials(customerName);
const html = `
<!-- Header Section -->
<div class="detail-header">
<div style="display: flex; align-items: center; gap: var(--spacing-md); margin-bottom: var(--spacing-sm);">
<div class="customer-card-avatar" style="width: 64px; height: 64px; font-size: 24px;">
${escapeHtml(initials)}
</div>
<div>
<h1 class="detail-title">${escapeHtml(customerName)}</h1>
<div class="detail-subtitle">Customer ID: ${escapeHtml(customer.id?.toString() || 'N/A')}</div>
</div>
</div>
</div>
<!-- Contact Information Section -->
${renderContactSection(customer)}
<!-- Addresses Section -->
${renderAddressesSection(customer)}
`;
container.innerHTML = html;
}
// Listen for the set_globals event (fired when data updates)
window.addEventListener(SET_GLOBALS_EVENT_TYPE, (event) => {
console.log('[Fergus MCP] openai:set_globals event received');
console.log('[Fergus MCP] event.detail.globals:', event.detail.globals);
// Re-render when globals change
render();
});
// Initial render on load (in case data is already available)
console.log('[Fergus MCP] Customer detail template loaded');
console.log('[Fergus MCP] window.openai:', window.openai);
// Try rendering immediately if window.openai exists
if (window.openai) {
render();
}
</script>
</body>
</html>