Add sorting and fix remaining data processing, style cards
This commit is contained in:
176
examples DO NOT USE OR EDIT/EXAMPLE ONLY metaAdsService.js
Normal file
176
examples DO NOT USE OR EDIT/EXAMPLE ONLY metaAdsService.js
Normal file
@@ -0,0 +1,176 @@
|
||||
import axios from "axios";
|
||||
|
||||
class MetaAdsService {
|
||||
constructor() {
|
||||
this.client = axios.create({
|
||||
baseURL: "/api/meta-ads",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// metaAdsService.js
|
||||
|
||||
processMetrics(campaign) {
|
||||
const insights = campaign.insights?.data?.[0] || {};
|
||||
const spend = parseFloat(insights.spend || 0);
|
||||
const impressions = parseInt(insights.impressions || 0);
|
||||
const clicks = parseInt(insights.clicks || 0);
|
||||
const reach = parseInt(insights.reach || 0);
|
||||
const cpc = parseFloat(insights.cpc || 0);
|
||||
const ctr = parseFloat(insights.ctr || 0);
|
||||
const cpm = parseFloat(insights.cpm || 0);
|
||||
const frequency = parseFloat(insights.frequency || 0);
|
||||
|
||||
// Purchase value and total purchases
|
||||
const purchaseValue = (insights.action_values || [])
|
||||
.filter(({ action_type }) => action_type === "purchase")
|
||||
.reduce((sum, { value }) => sum + parseFloat(value || 0), 0);
|
||||
|
||||
const totalPurchases = (insights.actions || [])
|
||||
.filter(({ action_type }) => action_type === "purchase")
|
||||
.reduce((sum, { value }) => sum + parseInt(value || 0), 0);
|
||||
|
||||
// Aggregate unique actions
|
||||
const actionMap = new Map();
|
||||
(insights.actions || []).forEach(({ action_type, value }) => {
|
||||
const currentValue = actionMap.get(action_type) || 0;
|
||||
actionMap.set(action_type, currentValue + parseInt(value || 0));
|
||||
});
|
||||
|
||||
const actions = Array.from(actionMap.entries()).map(
|
||||
([action_type, value]) => ({
|
||||
action_type,
|
||||
value,
|
||||
})
|
||||
);
|
||||
|
||||
// Map of cost per action
|
||||
const costPerActionMap = new Map();
|
||||
(insights.cost_per_action_type || []).forEach(({ action_type, value }) => {
|
||||
costPerActionMap.set(action_type, parseFloat(value || 0));
|
||||
});
|
||||
|
||||
// Total post engagements
|
||||
const totalPostEngagements = actionMap.get("post_engagement") || 0;
|
||||
|
||||
return {
|
||||
spend,
|
||||
impressions,
|
||||
clicks,
|
||||
reach,
|
||||
frequency,
|
||||
ctr,
|
||||
cpm,
|
||||
cpc,
|
||||
actions,
|
||||
costPerActionMap, // Include cost per action map
|
||||
purchaseValue,
|
||||
totalPurchases,
|
||||
totalPostEngagements, // Include total post engagements
|
||||
};
|
||||
}
|
||||
|
||||
getObjectiveAction(campaignObjective) {
|
||||
const objectiveMap = {
|
||||
OUTCOME_AWARENESS: { action_type: "impressions", label: "Impressions" },
|
||||
OUTCOME_ENGAGEMENT: {
|
||||
action_type: "post_engagement",
|
||||
label: "Post Engagements",
|
||||
},
|
||||
OUTCOME_TRAFFIC: { action_type: "link_click", label: "Link Clicks" },
|
||||
OUTCOME_LEADS: { action_type: "lead", label: "Leads" },
|
||||
OUTCOME_SALES: { action_type: "purchase", label: "Purchases" },
|
||||
MESSAGES: { action_type: "messages", label: "Messages" },
|
||||
// Add other objectives as needed
|
||||
};
|
||||
|
||||
return (
|
||||
objectiveMap[campaignObjective] || {
|
||||
action_type: "link_click",
|
||||
label: "Link Clicks",
|
||||
}
|
||||
);
|
||||
}
|
||||
calculateBudget(campaign) {
|
||||
if (campaign.daily_budget) {
|
||||
return { value: campaign.daily_budget / 100, type: "day" };
|
||||
}
|
||||
if (campaign.lifetime_budget) {
|
||||
return { value: campaign.lifetime_budget / 100, type: "lifetime" };
|
||||
}
|
||||
|
||||
const adsets = campaign.adsets?.data || [];
|
||||
const dailyTotal = adsets.reduce(
|
||||
(sum, adset) => sum + (adset.daily_budget || 0),
|
||||
0
|
||||
);
|
||||
const lifetimeTotal = adsets.reduce(
|
||||
(sum, adset) => sum + (adset.lifetime_budget || 0),
|
||||
0
|
||||
);
|
||||
|
||||
if (dailyTotal > 0) return { value: dailyTotal / 100, type: "day" };
|
||||
if (lifetimeTotal > 0)
|
||||
return { value: lifetimeTotal / 100, type: "lifetime" };
|
||||
|
||||
return { value: 0, type: "day" };
|
||||
}
|
||||
|
||||
processCampaignData(campaign) {
|
||||
const metrics = this.processMetrics(campaign);
|
||||
const budget = this.calculateBudget(campaign);
|
||||
const { action_type, label } = this.getObjectiveAction(campaign.objective);
|
||||
|
||||
// Get cost per result from costPerActionMap
|
||||
const costPerResult = metrics.costPerActionMap.get(action_type) || 0;
|
||||
|
||||
return {
|
||||
id: campaign.id,
|
||||
name: campaign.name,
|
||||
status: campaign.status,
|
||||
objective: label, // User-friendly objective label
|
||||
objectiveActionType: action_type, // Action type for metrics
|
||||
budget: budget.value,
|
||||
budgetType: budget.type,
|
||||
metrics: {
|
||||
...metrics,
|
||||
costPerResult,
|
||||
},
|
||||
};
|
||||
}
|
||||
async getCampaigns({ since, until }) {
|
||||
try {
|
||||
const response = await this.client.get("/campaigns", {
|
||||
params: { since, until },
|
||||
});
|
||||
|
||||
return response.data.map((campaign) =>
|
||||
this.processCampaignData(campaign)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error fetching campaigns:",
|
||||
error.response?.data || error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async getAccountInsights({ since, until }) {
|
||||
try {
|
||||
const response = await this.client.get("/account_insights", {
|
||||
params: { since, until },
|
||||
});
|
||||
return response.data; // This will be an object with reach and other fields
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error fetching account insights:",
|
||||
error.response?.data || error.message
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const metaAdsService = new MetaAdsService();
|
||||
Reference in New Issue
Block a user