A tonne of updates; site info, documentation, campaign phone numbers, soccial share buttons, and several other things
This commit is contained in:
@@ -90,6 +90,82 @@ async function cacheRepresentatives(postalCode, representatives, representData)
|
||||
}
|
||||
|
||||
class CampaignsController {
|
||||
// Get public campaigns (no authentication required)
|
||||
async getPublicCampaigns(req, res, next) {
|
||||
try {
|
||||
const campaigns = await nocoDB.getAllCampaigns();
|
||||
|
||||
// Filter to only active campaigns and normalize data structure
|
||||
const activeCampaigns = await Promise.all(
|
||||
campaigns
|
||||
.filter(campaign => {
|
||||
const status = normalizeStatus(campaign['Status'] || campaign.status);
|
||||
return status === 'active';
|
||||
})
|
||||
.map(async (campaign) => {
|
||||
const id = campaign.ID || campaign.Id || campaign.id;
|
||||
|
||||
// Debug: Log specific fields we're looking for
|
||||
console.log(`Campaign ${id}:`, {
|
||||
'Show Call Count': campaign['Show Call Count'],
|
||||
'show_call_count': campaign.show_call_count,
|
||||
'Show Email Count': campaign['Show Email Count'],
|
||||
'show_email_count': campaign.show_email_count
|
||||
});
|
||||
|
||||
// Get email count if show_email_count is enabled
|
||||
let emailCount = null;
|
||||
const showEmailCount = campaign['Show Email Count'] || campaign.show_email_count;
|
||||
console.log(`Getting email count for campaign ID: ${id}, showEmailCount: ${showEmailCount}`);
|
||||
if (showEmailCount && id != null) {
|
||||
emailCount = await nocoDB.getCampaignEmailCount(id);
|
||||
console.log(`Email count result: ${emailCount}`);
|
||||
}
|
||||
|
||||
// Get call count if show_call_count is enabled
|
||||
let callCount = null;
|
||||
const showCallCount = campaign['Show Call Count'] || campaign.show_call_count;
|
||||
console.log(`Getting call count for campaign ID: ${id}, showCallCount: ${showCallCount}`);
|
||||
if (showCallCount && id != null) {
|
||||
callCount = await nocoDB.getCampaignCallCount(id);
|
||||
console.log(`Call count result: ${callCount}`);
|
||||
}
|
||||
|
||||
const rawTargetLevels = campaign['Target Government Levels'] || campaign.target_government_levels;
|
||||
const normalizedTargetLevels = normalizeTargetLevels(rawTargetLevels);
|
||||
|
||||
// Return only public-facing information
|
||||
return {
|
||||
id,
|
||||
slug: campaign['Campaign Slug'] || campaign.slug,
|
||||
title: campaign['Campaign Title'] || campaign.title,
|
||||
description: campaign['Description'] || campaign.description,
|
||||
call_to_action: campaign['Call to Action'] || campaign.call_to_action,
|
||||
cover_photo: campaign['Cover Photo'] || campaign.cover_photo,
|
||||
show_email_count: showEmailCount,
|
||||
show_call_count: showCallCount,
|
||||
target_government_levels: normalizedTargetLevels,
|
||||
created_at: campaign.CreatedAt || campaign.created_at,
|
||||
emailCount,
|
||||
callCount
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
campaigns: activeCampaigns
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Get public campaigns error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to retrieve campaigns',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Get all campaigns (for admin panel)
|
||||
async getAllCampaigns(req, res, next) {
|
||||
try {
|
||||
@@ -249,9 +325,23 @@ class CampaignsController {
|
||||
let emailCount = null;
|
||||
const showEmailCount = campaign['Show Email Count'] || campaign.show_email_count;
|
||||
if (showEmailCount) {
|
||||
const id = campaign.Id ?? campaign.id;
|
||||
const id = campaign.ID || campaign.Id || campaign.id;
|
||||
console.log('Getting email count for campaign ID:', id);
|
||||
if (id != null) {
|
||||
emailCount = await nocoDB.getCampaignEmailCount(id);
|
||||
console.log('Email count result:', emailCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Get call count if enabled
|
||||
let callCount = null;
|
||||
const showCallCount = campaign['Show Call Count'] || campaign.show_call_count;
|
||||
if (showCallCount) {
|
||||
const id = campaign.ID || campaign.Id || campaign.id;
|
||||
console.log('Getting call count for campaign ID:', id);
|
||||
if (id != null) {
|
||||
callCount = await nocoDB.getCampaignCallCount(id);
|
||||
console.log('Call count result:', callCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,9 +364,11 @@ class CampaignsController {
|
||||
allow_mailto_link: campaign['Allow Mailto Link'] || campaign.allow_mailto_link,
|
||||
collect_user_info: campaign['Collect User Info'] || campaign.collect_user_info,
|
||||
show_email_count: campaign['Show Email Count'] || campaign.show_email_count,
|
||||
show_call_count: campaign['Show Call Count'] || campaign.show_call_count,
|
||||
allow_email_editing: campaign['Allow Email Editing'] || campaign.allow_email_editing,
|
||||
target_government_levels: normalizeTargetLevels(campaign['Target Government Levels'] || campaign.target_government_levels),
|
||||
emailCount
|
||||
emailCount,
|
||||
callCount
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -419,6 +511,10 @@ class CampaignsController {
|
||||
}
|
||||
}
|
||||
|
||||
// Track old slug for cascade updates
|
||||
const oldSlug = existingCampaign['Campaign Slug'] || existingCampaign.slug;
|
||||
let newSlug = oldSlug;
|
||||
|
||||
if (updates.title) {
|
||||
let slug = generateSlug(updates.title);
|
||||
|
||||
@@ -433,6 +529,7 @@ class CampaignsController {
|
||||
}
|
||||
}
|
||||
updates.slug = slug;
|
||||
newSlug = slug;
|
||||
}
|
||||
|
||||
if (updates.target_government_levels !== undefined) {
|
||||
@@ -472,6 +569,19 @@ class CampaignsController {
|
||||
|
||||
const campaign = await nocoDB.updateCampaign(id, updates);
|
||||
|
||||
// If slug changed, update references in related tables
|
||||
if (oldSlug && newSlug && oldSlug !== newSlug) {
|
||||
console.log(`Campaign slug changed from '${oldSlug}' to '${newSlug}', updating references...`);
|
||||
const cascadeResult = await nocoDB.updateCampaignSlugReferences(id, oldSlug, newSlug);
|
||||
|
||||
if (cascadeResult.success) {
|
||||
console.log(`Successfully updated slug references: ${cascadeResult.updatedCampaignEmails} campaign emails, ${cascadeResult.updatedCallLogs} call logs`);
|
||||
} else {
|
||||
console.warn(`Failed to update some slug references:`, cascadeResult.error);
|
||||
// Don't fail the main update - cascade is a best-effort operation
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
campaign: {
|
||||
@@ -892,6 +1002,74 @@ class CampaignsController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Track campaign phone call
|
||||
async trackCampaignCall(req, res, next) {
|
||||
try {
|
||||
const { slug } = req.params;
|
||||
const {
|
||||
representativeName,
|
||||
representativeTitle,
|
||||
phoneNumber,
|
||||
officeType,
|
||||
userEmail,
|
||||
userName,
|
||||
postalCode
|
||||
} = req.body;
|
||||
|
||||
// Validate required fields
|
||||
if (!representativeName || !phoneNumber) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Representative name and phone number are required'
|
||||
});
|
||||
}
|
||||
|
||||
// Get campaign
|
||||
const campaign = await nocoDB.getCampaignBySlug(slug);
|
||||
if (!campaign) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'Campaign not found'
|
||||
});
|
||||
}
|
||||
|
||||
const campaignStatus = normalizeStatus(campaign['Status'] || campaign.status);
|
||||
if (campaignStatus !== 'active') {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
error: 'Campaign is not currently active'
|
||||
});
|
||||
}
|
||||
|
||||
// Log the call
|
||||
await nocoDB.logCall({
|
||||
representativeName,
|
||||
representativeTitle: representativeTitle || null,
|
||||
phoneNumber,
|
||||
officeType: officeType || null,
|
||||
callerName: userName || null,
|
||||
callerEmail: userEmail || null,
|
||||
postalCode: postalCode || null,
|
||||
campaignId: campaign.ID || campaign.Id || campaign.id,
|
||||
campaignSlug: slug,
|
||||
callerIP: req.ip || req.connection?.remoteAddress || null,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Call tracked successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Track campaign call error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to track call',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export controller instance and upload middleware
|
||||
|
||||
@@ -180,6 +180,55 @@ class RepresentativesController {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async trackCall(req, res, next) {
|
||||
try {
|
||||
const {
|
||||
representativeName,
|
||||
representativeTitle,
|
||||
phoneNumber,
|
||||
officeType,
|
||||
userEmail,
|
||||
userName,
|
||||
postalCode
|
||||
} = req.body;
|
||||
|
||||
// Validate required fields
|
||||
if (!representativeName || !phoneNumber) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Representative name and phone number are required'
|
||||
});
|
||||
}
|
||||
|
||||
// Log the call
|
||||
await nocoDB.logCall({
|
||||
representativeName,
|
||||
representativeTitle: representativeTitle || null,
|
||||
phoneNumber,
|
||||
officeType: officeType || null,
|
||||
callerName: userName || null,
|
||||
callerEmail: userEmail || null,
|
||||
postalCode: postalCode || null,
|
||||
campaignId: null,
|
||||
campaignSlug: null,
|
||||
callerIP: req.ip || req.connection?.remoteAddress || null,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Call tracked successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Track call error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to track call',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new RepresentativesController();
|
||||
Reference in New Issue
Block a user