Files
leocrm/frontend/src/api/importExport.ts
T

206 lines
5.8 KiB
TypeScript
Raw Normal View History

import { apiClient } from './client';
// ─── Types ──────────────────────────────────────────────────────────────────
export interface ImportError {
row: number;
field: string;
message: string;
}
export interface ErrorReport {
total_errors: number;
errors: ImportError[];
}
export interface ImportResult {
total?: number;
succeeded?: number;
failed?: number;
status?: 'success' | 'partial_success' | 'failed' | 'pending' | 'processing' | 'completed';
dry_run?: boolean;
error_report?: ErrorReport;
created?: Record<string, unknown>[];
errors?: string[];
warnings?: string[];
rows?: Record<string, unknown>[];
preview?: boolean;
// Legacy compat
valid?: number;
invalid?: number;
job_id?: string;
message?: string;
}
export interface PreviewResult {
total_rows: number;
columns: string[];
preview_rows: Record<string, string>[];
mapping_suggestion: Record<string, string>;
target_fields: string[];
}
export interface ValidateResult {
total: number;
succeeded: number;
failed: number;
status: string;
dry_run: boolean;
error_report: ErrorReport;
created: Record<string, unknown>[];
}
export interface JobStatus {
status: 'pending' | 'processing' | 'completed' | 'partial_success' | 'failed';
progress?: number;
total?: number;
succeeded?: number;
failed?: number;
error_report?: ErrorReport;
created_count?: number;
error?: string;
}
export type EntityType = 'companies' | 'contacts';
export type ExportFormat = 'csv' | 'xlsx' | 'json';
// ─── Import ─────────────────────────────────────────────────────────────────
/**
* Import a CSV/JSON/XLSX file. When dryRun is true, a preview is returned without
* writing to the database.
*/
export async function importCsv(
file: File,
entityType: string,
dryRun: boolean,
fieldMapping?: Record<string, string>,
): Promise<ImportResult> {
const url = dryRun ? '/import/preview' : '/import';
const formData = new FormData();
formData.append('file', file);
formData.append('entity_type', entityType);
if (fieldMapping) {
formData.append('field_mapping', JSON.stringify(fieldMapping));
}
const response = await apiClient.post<ImportResult>(url, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
}
/**
* Preview a file: parse, return first 10 rows + columns + mapping suggestion.
*/
export async function previewImport(
file: File,
entityType: string,
): Promise<PreviewResult> {
const formData = new FormData();
formData.append('file', file);
formData.append('entity_type', entityType);
const response = await apiClient.post<PreviewResult>('/import/preview', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
}
/**
* Validate a file against mapping: returns error report without importing.
*/
export async function validateImport(
file: File,
entityType: string,
fieldMapping?: Record<string, string>,
): Promise<ValidateResult> {
const formData = new FormData();
formData.append('file', file);
formData.append('entity_type', entityType);
if (fieldMapping) {
formData.append('field_mapping', JSON.stringify(fieldMapping));
}
const response = await apiClient.post<ValidateResult>('/import/validate', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
}
/**
* Import contacts specifically.
*/
export async function importContacts(
file: File,
fieldMapping?: Record<string, string>,
): Promise<ImportResult> {
const formData = new FormData();
formData.append('file', file);
if (fieldMapping) {
formData.append('field_mapping', JSON.stringify(fieldMapping));
}
const response = await apiClient.post<ImportResult>('/import/contacts', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
}
/**
* Import companies specifically.
*/
export async function importCompanies(
file: File,
fieldMapping?: Record<string, string>,
): Promise<ImportResult> {
const formData = new FormData();
formData.append('file', file);
if (fieldMapping) {
formData.append('field_mapping', JSON.stringify(fieldMapping));
}
const response = await apiClient.post<ImportResult>('/import/companies', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
}
/**
* Get status of a background import job.
*/
export async function getImportJobStatus(jobId: string): Promise<JobStatus> {
const response = await apiClient.get<JobStatus>(`/import/status/${jobId}`);
return response.data;
}
// ─── Export ─────────────────────────────────────────────────────────────────
/**
* Export data as CSV, XLSX, or JSON and trigger a browser download.
*/
export async function exportData(
entityType: string,
format: string,
): Promise<void> {
const url = `/export?entity_type=${encodeURIComponent(entityType)}&format=${encodeURIComponent(format)}`;
const response = await apiClient.get(url, { responseType: 'blob' });
const blob = new Blob([response.data], {
type: format === 'xlsx'
? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
: format === 'json'
? 'application/json'
: 'text/csv',
});
const blobUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = `${entityType}_export.${format}`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(blobUrl);
}