diff --git a/dashboard-server/klaviyo-server/services/reporting.service.js b/dashboard-server/klaviyo-server/services/reporting.service.js index 5be3761..8731713 100644 --- a/dashboard-server/klaviyo-server/services/reporting.service.js +++ b/dashboard-server/klaviyo-server/services/reporting.service.js @@ -165,42 +165,72 @@ export class ReportingService { return []; } - const campaignDetails = await Promise.all( - campaignIds.map(async (campaignId) => { - const response = await fetch( - `${this.baseUrl}/campaigns/${campaignId}?include=campaign-messages`, - { - headers: { - 'Accept': 'application/json', - 'Authorization': `Klaviyo-API-Key ${this.apiKey}`, - 'revision': this.apiRevision + const fetchWithTimeout = async (campaignId, retries = 3) => { + for (let i = 0; i < retries; i++) { + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout + + const response = await fetch( + `${this.baseUrl}/campaigns/${campaignId}?include=campaign-messages`, + { + headers: { + 'Accept': 'application/json', + 'Authorization': `Klaviyo-API-Key ${this.apiKey}`, + 'revision': this.apiRevision + }, + signal: controller.signal } + ); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`Failed to fetch campaign ${campaignId}: ${response.status}`); } - ); - if (!response.ok) { - throw new Error(`Failed to fetch campaign ${campaignId}: ${response.status}`); - } - - const data = await response.json(); - if (!data.data) { - throw new Error(`Invalid response for campaign ${campaignId}`); - } - - const message = data.included?.find(item => item.type === 'campaign-message'); - - return { - id: data.data.id, - type: data.data.type, - attributes: { - ...data.data.attributes, - name: data.data.attributes.name, - send_time: data.data.attributes.send_time, - subject: message?.attributes?.content?.subject + const data = await response.json(); + if (!data.data) { + throw new Error(`Invalid response for campaign ${campaignId}`); } - }; - }) - ); + + const message = data.included?.find(item => item.type === 'campaign-message'); + + return { + id: data.data.id, + type: data.data.type, + attributes: { + ...data.data.attributes, + name: data.data.attributes.name, + send_time: data.data.attributes.send_time, + subject: message?.attributes?.content?.subject + } + }; + } catch (error) { + if (i === retries - 1) throw error; + await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // Exponential backoff + } + } + }; + + // Process in smaller chunks to avoid overwhelming the API + const chunkSize = 10; + const campaignDetails = []; + + for (let i = 0; i < campaignIds.length; i += chunkSize) { + const chunk = campaignIds.slice(i, i + chunkSize); + const results = await Promise.all( + chunk.map(id => fetchWithTimeout(id).catch(error => { + console.error(`Failed to fetch campaign ${id}:`, error); + return null; + })) + ); + campaignDetails.push(...results.filter(Boolean)); + + if (i + chunkSize < campaignIds.length) { + await new Promise(resolve => setTimeout(resolve, 1000)); // 1 second delay between chunks + } + } return campaignDetails; } diff --git a/dashboard/src/lib/constants.js b/dashboard/src/lib/constants.js index 37d2e8b..853084a 100644 --- a/dashboard/src/lib/constants.js +++ b/dashboard/src/lib/constants.js @@ -2,12 +2,12 @@ export const TIME_RANGES = [ { value: 'today', label: 'Today' }, { value: 'yesterday', label: 'Yesterday' }, { value: 'last7days', label: 'Last 7 Days' }, - { value: 'last14days', label: 'Last 14 Days' }, { value: 'last30days', label: 'Last 30 Days' }, { value: 'last90days', label: 'Last 90 Days' }, - { value: 'monthToDate', label: 'Month to Date' }, - { value: 'quarterToDate', label: 'Quarter to Date' }, - { value: 'yearToDate', label: 'Year to Date' }, + { value: 'thisWeek', label: 'This Week' }, + { value: 'lastWeek', label: 'Last Week' }, + { value: 'thisMonth', label: 'This Month' }, + { value: 'lastMonth', label: 'Last Month' } ]; export const GROUP_BY_OPTIONS = [