feat(campaigns): add frontend campaign API module and types

Adds the typed API client for campaign management (Task 7): campaign.ts
types, campaignApi module wired to the backend's /campaigns endpoints,
and registration in the aggregated api index. This is the API layer
only; the list page and wizard land in Tasks 8/9.
This commit is contained in:
AFFAANh
2026-08-01 14:09:08 +05:30
parent b42b7406dc
commit 8145fcb52d
3 changed files with 164 additions and 0 deletions
+4
View File
@@ -22,6 +22,7 @@ import { diagnosticsApi } from "./modules/diagnostics";
import { personaApi } from "./modules/persona";
import { brandApi } from "./modules/brand";
import { crmApi } from "./modules/crm";
import { campaignApi } from "./modules/campaign";
export const api = {
// Root
@@ -80,6 +81,9 @@ export const api = {
// First-party CRM
...crmApi,
// Campaigns
...campaignApi,
};
export default api;
+70
View File
@@ -0,0 +1,70 @@
import { request } from "../request";
import type {
AdAccount,
Campaign,
CampaignEvent,
CampaignPayload,
PreviewResponse,
} from "../types/campaign";
export const campaignApi = {
listCampaigns: (params?: { company_id?: string; status_filter?: string }) => {
const search = new URLSearchParams();
if (params?.company_id) search.append("company_id", params.company_id);
if (params?.status_filter) search.append("status_filter", params.status_filter);
const query = search.toString();
return request<Campaign[]>(`/campaigns${query ? `?${query}` : ""}`);
},
getCampaign: (id: string) =>
request<Campaign>(`/campaigns/${encodeURIComponent(id)}`),
createCampaign: (payload: CampaignPayload) =>
request<Campaign>("/campaigns", {
method: "POST",
body: JSON.stringify(payload),
}),
updateCampaign: (id: string, payload: CampaignPayload) =>
request<Campaign>(`/campaigns/${encodeURIComponent(id)}`, {
method: "PUT",
body: JSON.stringify(payload),
}),
deleteCampaign: (id: string) =>
request<void>(`/campaigns/${encodeURIComponent(id)}`, { method: "DELETE" }),
submitCampaign: (id: string, actor?: string) =>
request<Campaign>(`/campaigns/${encodeURIComponent(id)}/submit`, {
method: "POST",
body: JSON.stringify({ actor: actor ?? null }),
}),
approveCampaign: (id: string, actor?: string) =>
request<Campaign>(`/campaigns/${encodeURIComponent(id)}/approve`, {
method: "POST",
body: JSON.stringify({ actor: actor ?? null }),
}),
rejectCampaign: (id: string, reason?: string) =>
request<Campaign>(`/campaigns/${encodeURIComponent(id)}/reject`, {
method: "POST",
body: JSON.stringify({ reason: reason ?? null }),
}),
previewCampaign: (payload: {
ad_account_id: string;
creative: Record<string, unknown>;
ad_formats?: string[];
}) =>
request<PreviewResponse>("/campaigns/preview", {
method: "POST",
body: JSON.stringify(payload),
}),
getAdAccount: (adAccountId: string) =>
request<AdAccount>(`/campaigns/account/${encodeURIComponent(adAccountId)}`),
listCampaignEvents: (id: string) =>
request<CampaignEvent[]>(`/campaigns/${encodeURIComponent(id)}/events`),
};
+90
View File
@@ -0,0 +1,90 @@
export type CampaignStatus =
| "draft"
| "pending_approval"
| "approved"
| "synced"
| "live"
| "paused"
| "stopped"
| "archived";
export interface CampaignBudget {
daily_budget?: number;
lifetime_budget?: number;
currency?: string;
bid_strategy?: string;
bid_amount?: number;
}
export interface CampaignGuardrails {
daily_budget_limit?: number;
lifetime_budget_limit?: number;
max_campaign_spend?: number;
max_cost_per_lead?: number;
max_cost_per_click?: number;
auto_pause_if_spend_reaches?: number;
require_approval_for_budget_increase?: boolean;
stop_loss_enabled?: boolean;
}
export interface Campaign {
id: string;
company_id: string | null;
name: string;
status: CampaignStatus;
origin: "maskanx" | "imported";
objective: string | null;
ad_account_id: string | null;
budget: CampaignBudget;
guardrails: CampaignGuardrails;
targeting: Record<string, unknown>;
advanced: Record<string, unknown>;
channels: string[];
schedule: Record<string, unknown>;
meta_campaign_id: string | null;
sync_status: string;
sync_error: string | null;
approved_by: string | null;
created_at: string | null;
updated_at: string | null;
}
export interface CampaignPayload {
name: string;
company_id?: string | null;
objective?: string | null;
ad_account_id?: string | null;
budget: CampaignBudget;
guardrails: CampaignGuardrails;
targeting?: Record<string, unknown>;
advanced?: Record<string, unknown>;
channels?: string[];
schedule?: Record<string, unknown>;
}
export interface AdAccount {
id: string;
name?: string;
currency?: string;
balance?: string;
amount_spent?: string;
spend_cap?: string;
min_daily_budget?: number;
is_prepay_account?: boolean;
funding_source?: string;
account_status?: number;
}
export interface PreviewResponse {
previews: Record<string, string>;
}
export interface CampaignEvent {
id: string;
campaign_id: string;
event_type: string;
actor: string | null;
reason: string | null;
payload: Record<string, unknown>;
occurred_at: string;
}