diff --git a/frontend/src/api/campaigns.ts b/frontend/src/api/campaigns.ts index b815033..e7795c2 100644 --- a/frontend/src/api/campaigns.ts +++ b/frontend/src/api/campaigns.ts @@ -34,6 +34,9 @@ export interface Campaign { start_date: string | null; scheduled_at: string | null; completed_at: string | null; + approved_by: string | null; + approved_at: string | null; + rejection_reason: string | null; target_platform: string | null; tags: string[]; created_at: string | null; @@ -65,7 +68,6 @@ export interface CampaignSummary { export interface CampaignCreatePayload { name: string; description?: string; - start_date?: string; // ISO date YYYY-MM-DD — campaign won't activate before this type?: string; threat_actor_id?: string; target_platform?: string; @@ -80,6 +82,35 @@ export interface AddTestPayload { phase?: string; } +export type ModificationRequestAction = "add_test" | "remove_test"; +export type ModificationRequestStatus = "pending" | "approved" | "rejected"; + +export interface CampaignModificationRequest { + id: string; + campaign_id: string; + campaign_name: string | null; + requested_by: string | null; + action: ModificationRequestAction; + test_id: string | null; + test_name: string | null; + order_index: number | null; + phase: string | null; + justification: string; + status: ModificationRequestStatus; + reviewed_by: string | null; + reviewed_at: string | null; + review_notes: string | null; + created_at: string | null; +} + +export interface CampaignTimelineEntry { + id: string; + action: string; + user_id: string | null; + timestamp: string; + details: Record; +} + // ── API Functions ─────────────────────────────────────────────────── /** List campaigns with optional filters. */ @@ -247,3 +278,93 @@ export async function getCampaignHistory(campaignId: string): Promise<{ const { data } = await client.get(`/campaigns/${campaignId}/history`); return data; } + +// ── Approval workflow ────────────────────────────────────────────── + +/** Submit a draft campaign into the manager's approval queue. */ +export async function submitCampaignForApproval(campaignId: string): Promise { + const { data } = await client.post(`/campaigns/${campaignId}/submit`); + return data; +} + +/** Manager approves a pending campaign, fixing its start date. */ +export async function approveCampaign(campaignId: string, startDate: string): Promise { + const { data } = await client.post(`/campaigns/${campaignId}/approve`, { + start_date: startDate, + }); + return data; +} + +/** Manager rejects a pending campaign with a mandatory reason. */ +export async function rejectCampaign(campaignId: string, reason: string): Promise { + const { data } = await client.post(`/campaigns/${campaignId}/reject`, { reason }); + return data; +} + +// ── Modification requests ─────────────────────────────────────────── + +/** File a request to add/remove a test on an active campaign. */ +export async function createModificationRequest( + campaignId: string, + payload: { + action: ModificationRequestAction; + test_id: string; + justification: string; + order_index?: number; + phase?: string; + }, +): Promise { + const { data } = await client.post( + `/campaigns/${campaignId}/modification-requests`, + payload, + ); + return data; +} + +/** List modification requests filed against a specific campaign. */ +export async function listCampaignModificationRequests( + campaignId: string, +): Promise { + const { data } = await client.get( + `/campaigns/${campaignId}/modification-requests`, + ); + return data; +} + +/** Manager's global queue of modification requests awaiting review. */ +export async function listPendingModificationRequests(): Promise { + const { data } = await client.get( + "/campaigns/modification-requests/pending", + ); + return data; +} + +/** Manager approves a modification request — the test change is applied now. */ +export async function approveModificationRequest( + requestId: string, +): Promise { + const { data } = await client.post( + `/campaigns/modification-requests/${requestId}/approve`, + ); + return data; +} + +/** Manager rejects a modification request with mandatory review notes. */ +export async function rejectModificationRequest( + requestId: string, + reviewNotes: string, +): Promise { + const { data } = await client.post( + `/campaigns/modification-requests/${requestId}/reject`, + { review_notes: reviewNotes }, + ); + return data; +} + +// ── Timeline ───────────────────────────────────────────────────────── + +/** Get the audit-log timeline for a campaign. */ +export async function getCampaignTimeline(campaignId: string): Promise { + const { data } = await client.get(`/campaigns/${campaignId}/timeline`); + return data; +}