/**
* GoingsOn - Contacts Render Module
* Contact card rendering, detail modal rendering
*/
(function() {
'use strict';
const esc = GoingsOn.utils.escapeHtml;
const escAttr = GoingsOn.utils.escapeAttr;
// ============ Helpers ============
/**
* Extract up to 2 initials from a display name.
* @param {string} name - Full display name
* @returns {string} Uppercase initials (e.g. "JD")
*/
function getInitials(name) {
return name.split(/\s+/).filter(Boolean).map(w => w[0]).slice(0, 2).join('').toUpperCase();
}
// ============ Card Rendering ============
/**
* Render a contact card for the contacts grid.
* @param {Object} c - Contact object
* @returns {string} HTML string for the contact card
*/
function renderCard(c) {
const initials = c.initials || getInitials(c.displayName);
const primaryEmail = c.primaryEmail || c.emails?.find(e => e.isPrimary)?.address || c.emails?.[0]?.address || '';
const nickname = c.nickname ? `"${esc(c.nickname)}"` : '';
const company = c.company ? `${esc(c.company)}` : '';
const emailLine = primaryEmail ? `${esc(primaryEmail)}` : '';
const tagPills = (c.tags || []).map(t =>
`${esc(t)}`
).join('');
return `
${esc(initials)}
${esc(c.displayName)}
${nickname}
${company}
${emailLine ? `
${emailLine}
` : ''}
${tagPills ? `
${tagPills}
` : ''}
`;
}
// ============ Detail Modal Rendering ============
/**
* Show the full contact detail modal with all sub-collections.
* @param {Object} contact - Full contact object with emails, phones, socialHandles, customFields
*/
function showDetailModal(contact) {
const initials = contact.initials || getInitials(contact.displayName);
// Build info section
let info = '';
if (contact.nickname) info += `