tonne of imporvements, debugs, new ui
This commit is contained in:
@@ -49,8 +49,8 @@ class CampaignPage {
|
||||
|
||||
renderCampaign() {
|
||||
// Update page title and header
|
||||
document.title = `${this.campaign.title} - Alberta Influence Tool`;
|
||||
document.getElementById('page-title').textContent = `${this.campaign.title} - Alberta Influence Tool`;
|
||||
document.title = `${this.campaign.title} - BNKops Influence Tool`;
|
||||
document.getElementById('page-title').textContent = `${this.campaign.title} - BNKops Influence Tool`;
|
||||
document.getElementById('campaign-title').textContent = this.campaign.title;
|
||||
document.getElementById('campaign-description').textContent = this.campaign.description;
|
||||
|
||||
@@ -74,6 +74,15 @@ class CampaignPage {
|
||||
// Set up email method options
|
||||
this.setupEmailMethodOptions();
|
||||
|
||||
// Show optional fields if user info collection is enabled
|
||||
if (this.campaign.collect_user_info) {
|
||||
const optionalFields = document.getElementById('optional-fields');
|
||||
if (optionalFields) {
|
||||
optionalFields.style.display = 'block';
|
||||
console.log('Showing optional user info fields');
|
||||
}
|
||||
}
|
||||
|
||||
// Set initial step
|
||||
this.setStep(1);
|
||||
}
|
||||
@@ -131,9 +140,38 @@ class CampaignPage {
|
||||
userEmail: formData.get('userEmail') || ''
|
||||
};
|
||||
|
||||
// Track user info when they click "Find My Representatives"
|
||||
await this.trackUserInfo();
|
||||
|
||||
await this.loadRepresentatives();
|
||||
}
|
||||
|
||||
async trackUserInfo() {
|
||||
try {
|
||||
const response = await fetch(`/api/campaigns/${this.campaignSlug}/track-user`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userEmail: this.userInfo.userEmail,
|
||||
userName: this.userInfo.userName,
|
||||
postalCode: this.userInfo.postalCode
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.success) {
|
||||
console.warn('Failed to track user info:', data.error);
|
||||
// Don't throw error - this is just tracking, shouldn't block the user
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to track user info:', error.message);
|
||||
// Don't throw error - this is just tracking, shouldn't block the user
|
||||
}
|
||||
}
|
||||
|
||||
async loadRepresentatives() {
|
||||
this.showLoading('Finding your representatives...');
|
||||
|
||||
@@ -180,10 +218,11 @@ class CampaignPage {
|
||||
<p>${rep.elected_office || 'Representative'}</p>
|
||||
<p>${rep.party_name || ''}</p>
|
||||
${rep.email ? `<p>📧 ${rep.email}</p>` : ''}
|
||||
${this.getPhoneNumber(rep) ? `<p>📞 ${this.getPhoneNumber(rep)}</p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
${rep.email ? `
|
||||
<div class="rep-actions">
|
||||
<div class="rep-actions">
|
||||
${rep.email ? `
|
||||
<button class="btn btn-primary" data-action="send-email"
|
||||
data-email="${rep.email}"
|
||||
data-name="${rep.name}"
|
||||
@@ -191,8 +230,18 @@ class CampaignPage {
|
||||
data-level="${this.getGovernmentLevel(rep)}">
|
||||
Send Email
|
||||
</button>
|
||||
</div>
|
||||
` : '<p style="text-align: center; color: #6c757d;">No email available</p>'}
|
||||
` : ''}
|
||||
${this.getPhoneNumber(rep) ? `
|
||||
<button class="btn btn-success" data-action="call-representative"
|
||||
data-phone="${this.getPhoneNumber(rep)}"
|
||||
data-name="${rep.name}"
|
||||
data-title="${rep.elected_office || ''}"
|
||||
data-office-type="${this.getPhoneOfficeType(rep)}">
|
||||
📞 Call
|
||||
</button>
|
||||
` : ''}
|
||||
${!rep.email && !this.getPhoneNumber(rep) ? '<p style="text-align: center; color: #6c757d;">No contact information available</p>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
@@ -214,6 +263,17 @@ class CampaignPage {
|
||||
});
|
||||
});
|
||||
|
||||
// Call buttons
|
||||
document.querySelectorAll('[data-action="call-representative"]').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
const phone = e.target.dataset.phone;
|
||||
const name = e.target.dataset.name;
|
||||
const title = e.target.dataset.title;
|
||||
const officeType = e.target.dataset.officeType;
|
||||
this.callRepresentative(phone, name, title, officeType);
|
||||
});
|
||||
});
|
||||
|
||||
// Reload page button
|
||||
const reloadBtn = document.querySelector('[data-action="reload-page"]');
|
||||
if (reloadBtn) {
|
||||
@@ -232,6 +292,67 @@ class CampaignPage {
|
||||
return 'Other';
|
||||
}
|
||||
|
||||
getPhoneNumber(rep) {
|
||||
if (!rep.offices || !Array.isArray(rep.offices)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the first office with a phone number
|
||||
const officeWithPhone = rep.offices.find(office => office.tel);
|
||||
return officeWithPhone ? officeWithPhone.tel : null;
|
||||
}
|
||||
|
||||
getPhoneOfficeType(rep) {
|
||||
if (!rep.offices || !Array.isArray(rep.offices)) {
|
||||
return 'office';
|
||||
}
|
||||
|
||||
const officeWithPhone = rep.offices.find(office => office.tel);
|
||||
return officeWithPhone ? (officeWithPhone.type || 'office') : 'office';
|
||||
}
|
||||
|
||||
callRepresentative(phone, name, title, officeType) {
|
||||
// Clean the phone number for tel: link (remove spaces, dashes, parentheses)
|
||||
const cleanPhone = phone.replace(/[\s\-\(\)]/g, '');
|
||||
|
||||
// Create tel: link
|
||||
const telLink = `tel:${cleanPhone}`;
|
||||
|
||||
// Show confirmation dialog with formatted information
|
||||
const officeInfo = officeType ? ` (${officeType} office)` : '';
|
||||
const message = `Call ${name}${title ? ` - ${title}` : ''}${officeInfo}?\n\nPhone: ${phone}`;
|
||||
|
||||
if (confirm(message)) {
|
||||
// Attempt to initiate the call
|
||||
window.location.href = telLink;
|
||||
|
||||
// Track the call attempt
|
||||
this.trackCall(phone, name, title, officeType);
|
||||
}
|
||||
}
|
||||
|
||||
async trackCall(phone, name, title, officeType) {
|
||||
try {
|
||||
await fetch(`/api/campaigns/${this.campaignSlug}/track-call`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userEmail: this.userInfo.userEmail,
|
||||
userName: this.userInfo.userName,
|
||||
postalCode: this.userInfo.postalCode,
|
||||
recipientPhone: phone,
|
||||
recipientName: name,
|
||||
recipientTitle: title,
|
||||
officeType: officeType
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to track call:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async sendEmail(recipientEmail, recipientName, recipientTitle, recipientLevel) {
|
||||
const emailMethod = document.querySelector('input[name="emailMethod"]:checked').value;
|
||||
|
||||
|
||||
@@ -107,6 +107,10 @@ class RepresentativesDisplay {
|
||||
const party = rep.party_name || 'Party not specified';
|
||||
const photoUrl = rep.photo_url || null;
|
||||
|
||||
// Extract phone numbers from offices array
|
||||
const phoneNumbers = this.extractPhoneNumbers(rep.offices || []);
|
||||
const primaryPhone = phoneNumbers.length > 0 ? phoneNumbers[0] : null;
|
||||
|
||||
const emailButton = email ?
|
||||
`<button class="btn btn-primary compose-email"
|
||||
data-email="${email}"
|
||||
@@ -117,6 +121,16 @@ class RepresentativesDisplay {
|
||||
</button>` :
|
||||
'<span class="text-muted">No email available</span>';
|
||||
|
||||
// Add call button if phone number is available
|
||||
const callButton = primaryPhone ?
|
||||
`<button class="btn btn-success call-representative"
|
||||
data-phone="${primaryPhone.number}"
|
||||
data-office-type="${primaryPhone.type}"
|
||||
data-name="${name}"
|
||||
data-office="${office}">
|
||||
📞 Call
|
||||
</button>` : '';
|
||||
|
||||
const profileUrl = rep.url ?
|
||||
`<a href="${rep.url}" target="_blank" class="btn btn-secondary">View Profile</a>` : '';
|
||||
|
||||
@@ -155,9 +169,11 @@ class RepresentativesDisplay {
|
||||
<p><strong>District:</strong> ${district}</p>
|
||||
${party !== 'Party not specified' ? `<p><strong>Party:</strong> ${party}</p>` : ''}
|
||||
${email ? `<p><strong>Email:</strong> ${email}</p>` : ''}
|
||||
${primaryPhone ? `<p><strong>Phone:</strong> ${primaryPhone.number} ${primaryPhone.type ? `(${primaryPhone.type})` : ''}</p>` : ''}
|
||||
</div>
|
||||
<div class="rep-actions">
|
||||
${emailButton}
|
||||
${callButton}
|
||||
${profileUrl}
|
||||
</div>
|
||||
</div>
|
||||
@@ -165,6 +181,23 @@ class RepresentativesDisplay {
|
||||
`;
|
||||
}
|
||||
|
||||
extractPhoneNumbers(offices) {
|
||||
const phoneNumbers = [];
|
||||
|
||||
if (Array.isArray(offices)) {
|
||||
offices.forEach(office => {
|
||||
if (office.tel) {
|
||||
phoneNumbers.push({
|
||||
number: office.tel,
|
||||
type: office.type || 'office'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return phoneNumbers;
|
||||
}
|
||||
|
||||
attachEventListeners() {
|
||||
// Add event listeners for compose email buttons
|
||||
const composeButtons = this.container.querySelectorAll('.compose-email');
|
||||
@@ -183,6 +216,36 @@ class RepresentativesDisplay {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Add event listeners for call buttons
|
||||
const callButtons = this.container.querySelectorAll('.call-representative');
|
||||
callButtons.forEach(button => {
|
||||
button.addEventListener('click', (e) => {
|
||||
const phone = e.target.dataset.phone;
|
||||
const name = e.target.dataset.name;
|
||||
const office = e.target.dataset.office;
|
||||
const officeType = e.target.dataset.officeType;
|
||||
|
||||
this.handleCallClick(phone, name, office, officeType);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleCallClick(phone, name, office, officeType) {
|
||||
// Clean the phone number for tel: link (remove spaces, dashes, parentheses)
|
||||
const cleanPhone = phone.replace(/[\s\-\(\)]/g, '');
|
||||
|
||||
// Create tel: link
|
||||
const telLink = `tel:${cleanPhone}`;
|
||||
|
||||
// Show confirmation dialog with formatted information
|
||||
const officeInfo = officeType ? ` (${officeType} office)` : '';
|
||||
const message = `Call ${name}${officeInfo}?\n\nPhone: ${phone}`;
|
||||
|
||||
if (confirm(message)) {
|
||||
// Attempt to initiate the call
|
||||
window.location.href = telLink;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user