Response wall build out

This commit is contained in:
2025-10-11 22:56:48 -06:00
parent ac54d998c8
commit cece612772
7 changed files with 556 additions and 27 deletions

View File

@@ -291,7 +291,7 @@ async function getResponseStats(req, res) {
// Get all approved responses for this campaign
const responses = await nocodbService.getRepresentativeResponses({
where: `(Campaign Slug,eq,${slug}),(Status,eq,approved)`
where: `(Campaign Slug,eq,${slug})~and(Status,eq,approved)`
});
// Calculate stats
@@ -323,43 +323,91 @@ async function getResponseStats(req, res) {
}
}
// Admin functions
// Admin and campaign owner functions
/**
* Get all responses (admin only - includes pending/rejected)
* Get all responses (admin or campaign owners - includes pending/rejected)
* Campaign owners only see responses to their own campaigns
* @param {Object} req - Express request object
* @param {Object} res - Express response object
*/
async function getAdminResponses(req, res) {
try {
const { status = 'all', offset = 0, limit = 50 } = req.query;
const { status = 'all', campaign_slug = '', offset = 0, limit = 50 } = req.query;
console.log(`Admin fetching responses with status filter: ${status}`);
console.log(`User fetching responses with status filter: ${status}, campaign: ${campaign_slug}`);
const isAdmin = (req.user && req.user.isAdmin) || req.session?.isAdmin || false;
const sessionUserId = req.user?.id ?? req.session?.userId ?? null;
const sessionUserEmail = req.user?.email ?? req.session?.userEmail ?? null;
// If not admin, we need to filter by campaign ownership
let campaignsToFilter = [];
if (!isAdmin) {
// Get all campaigns owned by this user
const allCampaigns = await nocodbService.getAllCampaigns();
campaignsToFilter = allCampaigns.filter(campaign => {
const createdById = campaign['Created By User ID'] ?? campaign.created_by_user_id ?? null;
const createdByEmail = campaign['Created By User Email'] ?? campaign.created_by_user_email ?? null;
return (
(createdById != null && sessionUserId != null && String(createdById) === String(sessionUserId)) ||
(createdByEmail && sessionUserEmail && String(createdByEmail).toLowerCase() === String(sessionUserEmail).toLowerCase())
);
});
console.log(`User owns ${campaignsToFilter.length} campaigns out of ${allCampaigns.length} total`);
}
let whereConditions = [];
// Filter by status
if (status !== 'all') {
whereConditions.push(`(Status,eq,${status})`);
}
// Filter by campaign slug if provided
if (campaign_slug) {
const slugCondition = `(Campaign Slug,eq,${campaign_slug})`;
if (whereConditions.length > 0) {
whereConditions[0] += `~and${slugCondition}`;
} else {
whereConditions.push(slugCondition);
}
}
const responses = await nocodbService.getRepresentativeResponses({
where: whereConditions.join(','),
where: whereConditions.length > 0 ? whereConditions[0] : undefined,
sort: '-CreatedAt',
offset: parseInt(offset),
limit: parseInt(limit)
});
console.log(`Admin found ${responses.length} total responses`);
if (responses.length > 0) {
console.log('Response statuses:', responses.map(r => `ID:${r.id} Status:${r.status}`).join(', '));
console.log(`Found ${responses.length} total responses before filtering`);
// Filter responses by campaign ownership if not admin
let filteredResponses = responses;
if (!isAdmin && campaignsToFilter.length > 0) {
const ownedCampaignSlugs = campaignsToFilter.map(c => c['Campaign Slug'] || c.slug);
filteredResponses = responses.filter(r => ownedCampaignSlugs.includes(r.campaign_slug));
console.log(`After ownership filter: ${filteredResponses.length} responses`);
} else if (!isAdmin) {
// User owns no campaigns, return empty
filteredResponses = [];
}
if (filteredResponses.length > 0) {
console.log('Response statuses:', filteredResponses.map(r => `ID:${r.id} Status:${r.status}`).join(', '));
}
res.json({
success: true,
responses,
responses: filteredResponses,
pagination: {
offset: parseInt(offset),
limit: parseInt(limit),
hasMore: responses.length === parseInt(limit)
hasMore: filteredResponses.length === parseInt(limit)
}
});
@@ -370,7 +418,7 @@ async function getAdminResponses(req, res) {
}
/**
* Update response status (admin only)
* Update response status (admin or campaign owner)
* @param {Object} req - Express request object
* @param {Object} res - Express response object
*/
@@ -385,6 +433,38 @@ async function updateResponseStatus(req, res) {
return res.status(400).json({ error: 'Invalid status' });
}
// Get the response to check campaign ownership
const response = await nocodbService.getRepresentativeResponseById(id);
if (!response) {
return res.status(404).json({ error: 'Response not found' });
}
// Get campaign to check ownership
const campaignId = response.campaign_id;
const campaign = await nocodbService.getCampaignById(campaignId);
if (!campaign) {
return res.status(404).json({ error: 'Campaign not found' });
}
// Check if user is admin or campaign owner
const isAdmin = (req.user && req.user.isAdmin) || req.session?.isAdmin || false;
const sessionUserId = req.user?.id ?? req.session?.userId ?? null;
const sessionUserEmail = req.user?.email ?? req.session?.userEmail ?? null;
if (!isAdmin) {
const createdById = campaign['Created By User ID'] ?? campaign.created_by_user_id ?? null;
const createdByEmail = campaign['Created By User Email'] ?? campaign.created_by_user_email ?? null;
const ownsCampaign = (
(createdById != null && sessionUserId != null && String(createdById) === String(sessionUserId)) ||
(createdByEmail && sessionUserEmail && String(createdByEmail).toLowerCase() === String(sessionUserEmail).toLowerCase())
);
if (!ownsCampaign) {
return res.status(403).json({ error: 'You can only moderate responses to your own campaigns' });
}
}
const updated = await nocodbService.updateRepresentativeResponse(id, { status });
console.log('Updated response:', JSON.stringify(updated, null, 2));