feat: download campaign report

This commit is contained in:
Abhishek Kumar 2026-03-11 17:57:04 +05:30
parent ff92c6ae5c
commit 4d807266a7
12 changed files with 429 additions and 28 deletions

View file

@ -1,11 +1,12 @@
"use client";
import { ArrowLeft, Check, Clock, Pause, Pencil, Play, RefreshCw, X } from 'lucide-react';
import { ArrowLeft, Check, Clock, Download, Pause, Pencil, Play, RefreshCw, X } from 'lucide-react';
import { useParams, useRouter, useSearchParams } from 'next/navigation';
import { useCallback, useEffect, useState } from 'react';
import { toast } from 'sonner';
import {
downloadCampaignReportApiV1CampaignCampaignIdReportGet,
getCampaignApiV1CampaignCampaignIdGet,
getCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGet,
pauseCampaignApiV1CampaignCampaignIdPausePost,
@ -111,6 +112,40 @@ export default function CampaignDetailPage() {
}
};
// Handle download report
const handleDownloadReport = async () => {
if (!user) return;
try {
const accessToken = await getAccessToken();
const response = await downloadCampaignReportApiV1CampaignCampaignIdReportGet({
path: {
campaign_id: campaignId,
},
headers: {
'Authorization': `Bearer ${accessToken}`,
},
parseAs: 'blob',
});
if (response.data) {
const blob = response.data as Blob;
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `campaign_${campaignId}_report.csv`;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} else {
toast.error('Failed to download report');
}
} catch (error) {
console.error('Failed to download report:', error);
toast.error('Failed to download report');
}
};
// Handle start campaign
const handleStart = async () => {
if (!user) return;
@ -328,7 +363,13 @@ export default function CampaignDetailPage() {
</span>
</div>
</div>
{renderActionButton()}
<div className="flex items-center gap-2">
<Button variant="outline" onClick={handleDownloadReport}>
<Download className="h-4 w-4 mr-2" />
Download Report
</Button>
{renderActionButton()}
</div>
</div>
</div>

File diff suppressed because one or more lines are too long

View file

@ -3034,6 +3034,39 @@ export type GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrl
export type GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetResponse = GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetResponses[keyof GetCampaignSourceDownloadUrlApiV1CampaignCampaignIdSourceDownloadUrlGetResponses];
export type DownloadCampaignReportApiV1CampaignCampaignIdReportGetData = {
body?: never;
headers?: {
authorization?: string | null;
'X-API-Key'?: string | null;
};
path: {
campaign_id: number;
};
query?: never;
url: '/api/v1/campaign/{campaign_id}/report';
};
export type DownloadCampaignReportApiV1CampaignCampaignIdReportGetErrors = {
/**
* Not found
*/
404: unknown;
/**
* Validation Error
*/
422: HttpValidationError;
};
export type DownloadCampaignReportApiV1CampaignCampaignIdReportGetError = DownloadCampaignReportApiV1CampaignCampaignIdReportGetErrors[keyof DownloadCampaignReportApiV1CampaignCampaignIdReportGetErrors];
export type DownloadCampaignReportApiV1CampaignCampaignIdReportGetResponses = {
/**
* Successful Response
*/
200: unknown;
};
export type ListCredentialsApiV1CredentialsGetData = {
body?: never;
headers?: {

View file

@ -131,8 +131,10 @@ export function UserConfigProvider({ children }: { children: ReactNode }) {
});
if (response.error) {
let msg = 'Failed to save user configuration';
const detail = (response.error as unknown as { detail?: { errors: { model: string; message: string }[] } }).detail;
if (Array.isArray(detail)) {
const detail = (response.error as unknown as { detail?: string | { errors: { model: string; message: string }[] } }).detail;
if (typeof detail === 'string') {
msg = detail;
} else if (Array.isArray(detail)) {
msg = detail
.map((e: { model: string; message: string }) => `${e.model}: ${e.message}`)
.join('\n');