192 lines
7.1 KiB
JavaScript
192 lines
7.1 KiB
JavaScript
// Representatives Display Module
|
|
class RepresentativesDisplay {
|
|
constructor() {
|
|
this.container = document.getElementById('representatives-container');
|
|
}
|
|
|
|
displayRepresentatives(representatives) {
|
|
if (!representatives || representatives.length === 0) {
|
|
this.container.innerHTML = `
|
|
<div class="rep-category">
|
|
<h3>No Representatives Found</h3>
|
|
<p>No representatives were found for this postal code. This might be due to:</p>
|
|
<ul>
|
|
<li>The postal code is not in our database</li>
|
|
<li>Temporary API issues</li>
|
|
<li>The postal code is not currently assigned to electoral districts</li>
|
|
</ul>
|
|
<p>Please try again later or verify your postal code.</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
// Group representatives by level/type
|
|
const grouped = this.groupRepresentatives(representatives);
|
|
|
|
let html = '';
|
|
|
|
// Order of importance for display
|
|
const displayOrder = [
|
|
'Federal',
|
|
'Provincial',
|
|
'Municipal',
|
|
'School Board',
|
|
'Other'
|
|
];
|
|
|
|
displayOrder.forEach(level => {
|
|
if (grouped[level] && grouped[level].length > 0) {
|
|
html += this.renderRepresentativeCategory(level, grouped[level]);
|
|
}
|
|
});
|
|
|
|
this.container.innerHTML = html;
|
|
this.attachEventListeners();
|
|
}
|
|
|
|
groupRepresentatives(representatives) {
|
|
const groups = {
|
|
'Federal': [],
|
|
'Provincial': [],
|
|
'Municipal': [],
|
|
'School Board': [],
|
|
'Other': []
|
|
};
|
|
|
|
representatives.forEach(rep => {
|
|
const setName = rep.representative_set_name || '';
|
|
const office = rep.elected_office || '';
|
|
|
|
if (setName.toLowerCase().includes('house of commons') ||
|
|
setName.toLowerCase().includes('federal') ||
|
|
office.toLowerCase().includes('member of parliament') ||
|
|
office.toLowerCase().includes('mp')) {
|
|
groups['Federal'].push(rep);
|
|
} else if (setName.toLowerCase().includes('provincial') ||
|
|
setName.toLowerCase().includes('legislative assembly') ||
|
|
setName.toLowerCase().includes('mla') ||
|
|
office.toLowerCase().includes('mla')) {
|
|
groups['Provincial'].push(rep);
|
|
} else if (setName.toLowerCase().includes('municipal') ||
|
|
setName.toLowerCase().includes('city council') ||
|
|
setName.toLowerCase().includes('mayor') ||
|
|
office.toLowerCase().includes('councillor') ||
|
|
office.toLowerCase().includes('mayor')) {
|
|
groups['Municipal'].push(rep);
|
|
} else if (setName.toLowerCase().includes('school') ||
|
|
office.toLowerCase().includes('school') ||
|
|
office.toLowerCase().includes('trustee')) {
|
|
groups['School Board'].push(rep);
|
|
} else {
|
|
groups['Other'].push(rep);
|
|
}
|
|
});
|
|
|
|
return groups;
|
|
}
|
|
|
|
renderRepresentativeCategory(categoryName, representatives) {
|
|
const cards = representatives.map(rep => this.renderRepresentativeCard(rep)).join('');
|
|
|
|
return `
|
|
<div class="rep-category">
|
|
<h3>${categoryName} Representatives</h3>
|
|
<div class="rep-cards">
|
|
${cards}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
renderRepresentativeCard(rep) {
|
|
const name = rep.name || 'Name not available';
|
|
const email = rep.email || null;
|
|
const office = rep.elected_office || 'Office not specified';
|
|
const district = rep.district_name || 'District not specified';
|
|
const party = rep.party_name || 'Party not specified';
|
|
const photoUrl = rep.photo_url || null;
|
|
|
|
const emailButton = email ?
|
|
`<button class="btn btn-primary compose-email"
|
|
data-email="${email}"
|
|
data-name="${name}"
|
|
data-office="${office}"
|
|
data-district="${district}">
|
|
Send Email
|
|
</button>` :
|
|
'<span class="text-muted">No email available</span>';
|
|
|
|
const profileUrl = rep.url ?
|
|
`<a href="${rep.url}" target="_blank" class="btn btn-secondary">View Profile</a>` : '';
|
|
|
|
// Generate initials for fallback
|
|
const initials = name.split(' ')
|
|
.map(word => word.charAt(0))
|
|
.join('')
|
|
.toUpperCase()
|
|
.slice(0, 2);
|
|
|
|
const photoElement = photoUrl ?
|
|
`<div class="rep-photo">
|
|
<img src="${photoUrl}"
|
|
alt="${name}"
|
|
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"
|
|
loading="lazy">
|
|
<div class="rep-photo-fallback" style="display: none;">
|
|
${initials}
|
|
</div>
|
|
</div>` :
|
|
`<div class="rep-photo">
|
|
<div class="rep-photo-fallback">
|
|
${initials}
|
|
</div>
|
|
</div>`;
|
|
|
|
return `
|
|
<div class="rep-card">
|
|
${photoElement}
|
|
<div class="rep-content">
|
|
<div class="rep-header">
|
|
<h4>${name}</h4>
|
|
</div>
|
|
<div class="rep-info">
|
|
<p><strong>Office:</strong> ${office}</p>
|
|
<p><strong>District:</strong> ${district}</p>
|
|
${party !== 'Party not specified' ? `<p><strong>Party:</strong> ${party}</p>` : ''}
|
|
${email ? `<p><strong>Email:</strong> ${email}</p>` : ''}
|
|
</div>
|
|
<div class="rep-actions">
|
|
${emailButton}
|
|
${profileUrl}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
attachEventListeners() {
|
|
// Add event listeners for compose email buttons
|
|
const composeButtons = this.container.querySelectorAll('.compose-email');
|
|
composeButtons.forEach(button => {
|
|
button.addEventListener('click', (e) => {
|
|
const email = e.target.dataset.email;
|
|
const name = e.target.dataset.name;
|
|
const office = e.target.dataset.office;
|
|
const district = e.target.dataset.district;
|
|
|
|
window.emailComposer.openModal({
|
|
email,
|
|
name,
|
|
office,
|
|
district
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// Initialize when DOM is loaded
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
window.representativesDisplay = new RepresentativesDisplay();
|
|
}); |