Add new frontend - fix 22

This commit is contained in:
Urtzi Alfaro
2025-07-23 15:41:41 +02:00
parent 96b728441a
commit 94b39ebdfb
2 changed files with 257 additions and 108 deletions

View File

@@ -1,8 +1,6 @@
// src/api/services/DataService.ts
// frontend/src/api/services/dataService.ts - COMPLETE FIX
import { apiClient } from '../base/apiClient';
import {
ApiResponse
} from '../types/api';
import { ApiResponse } from '../types/api';
export interface DashboardStats {
totalSales: number;
@@ -21,12 +19,28 @@ export interface UploadResponse {
upload_id?: string;
}
// FIXED: Updated to match backend SalesValidationResult schema
export interface DataValidation {
valid: boolean;
errors: string[];
warnings: string[];
recordCount: number;
duplicates: number;
is_valid: boolean; // Changed from 'valid' to 'is_valid'
total_records: number; // Changed from 'recordCount' to 'total_records'
valid_records: number; // Added missing field
invalid_records: number; // Added missing field
errors: Array<{ // Changed from string[] to object array
row?: number;
field?: string;
message: string;
value?: any;
}>;
warnings: Array<{ // Changed from string[] to object array
row?: number;
field?: string;
message: string;
value?: any;
}>;
summary: { // Added missing summary field
[key: string]: any;
};
duplicates?: number; // Made optional, may not always be present
}
// Data types
@@ -61,6 +75,15 @@ export interface CreateSalesRequest {
date: string;
}
// FIXED: Interface for import data that matches backend SalesDataImport schema
export interface SalesDataImportRequest {
tenant_id: string;
data: string; // File content as string
data_format: 'csv' | 'json' | 'excel';
source?: string;
validate_only?: boolean;
}
export class DataService {
/**
* Upload sales history file
@@ -78,14 +101,80 @@ export class DataService {
}
/**
* Validate sales data before upload
* FIXED: Validate sales data before upload
* Backend expects JSON data with SalesDataImport structure, not a file upload
*/
async validateSalesData(file: File): Promise<DataValidation> {
const response = await apiClient.upload<ApiResponse<DataValidation>>(
'/api/v1/sales/import/validate-sales',
file
);
return response.data!;
try {
// Read file content
const fileContent = await this.readFileAsText(file);
// Determine file format from extension
const fileName = file.name.toLowerCase();
let dataFormat: 'csv' | 'json' | 'excel';
if (fileName.endsWith('.csv')) {
dataFormat = 'csv';
} else if (fileName.endsWith('.json')) {
dataFormat = 'json';
} else if (fileName.endsWith('.xlsx') || fileName.endsWith('.xls')) {
dataFormat = 'excel';
} else {
// Default to CSV if unable to determine
dataFormat = 'csv';
}
// FIXED: Use the correct endpoint and send JSON data instead of file upload
const importData: SalesDataImportRequest = {
tenant_id: '', // Will be set by backend from auth context
data: fileContent,
data_format: dataFormat,
validate_only: true
};
const response = await apiClient.post<ApiResponse<DataValidation>>(
'/api/v1/data/sales/import/validate', // Fixed endpoint path
importData
);
return response.data!;
} catch (error: any) {
// Handle validation errors gracefully
if (error.response?.status === 422) {
// Return a failed validation result instead of throwing
return {
is_valid: false,
total_records: 0,
valid_records: 0,
invalid_records: 0,
errors: [{
message: error.response?.data?.detail || 'Validation failed'
}],
warnings: [],
summary: {
validation_error: true,
message: error.response?.data?.detail || 'File validation failed'
}
};
}
throw error;
}
}
/**
* Helper method to read file as text
*/
private readFileAsText(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
resolve(event.target?.result as string);
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
reader.readAsText(file);
});
}
/**
@@ -129,66 +218,42 @@ export class DataService {
}
/**
* Update sales record
* FIXED: Import sales data (actual import after validation)
*/
async updateSalesRecord(
id: string,
updates: Partial<CreateSalesRequest>
): Promise<SalesRecord> {
const response = await apiClient.put<ApiResponse<SalesRecord>>(
`/api/v1/data/sales/${id}`,
updates
);
return response.data!;
}
async importSalesData(file: File): Promise<UploadResponse> {
try {
const fileContent = await this.readFileAsText(file);
// Determine file format
const fileName = file.name.toLowerCase();
let dataFormat: 'csv' | 'json' | 'excel';
if (fileName.endsWith('.csv')) {
dataFormat = 'csv';
} else if (fileName.endsWith('.json')) {
dataFormat = 'json';
} else if (fileName.endsWith('.xlsx') || fileName.endsWith('.xls')) {
dataFormat = 'excel';
} else {
dataFormat = 'csv';
}
/**
* Delete sales record
*/
async deleteSalesRecord(id: string): Promise<void> {
await apiClient.delete(`/api/v1/data/sales/${id}`);
}
const importData: SalesDataImportRequest = {
tenant_id: '', // Will be set by backend from auth context
data: fileContent,
data_format: dataFormat,
validate_only: false
};
/**
* Get weather data
*/
async getWeatherData(params?: {
startDate?: string;
endDate?: string;
location?: string;
}): Promise<WeatherData[]> {
const response = await apiClient.get<ApiResponse<WeatherData[]>>(
'/api/v1/data/weather',
{ params }
);
return response.data!;
}
/**
* Get traffic data
*/
async getTrafficData(params?: {
startDate?: string;
endDate?: string;
location?: string;
}): Promise<TrafficData[]> {
const response = await apiClient.get<ApiResponse<TrafficData[]>>(
'/api/v1/data/traffic',
{ params }
);
return response.data!;
}
/**
* Get data quality report
*/
async getDataQuality(): Promise<{
salesData: { completeness: number; quality: number; lastUpdate: string };
weatherData: { completeness: number; quality: number; lastUpdate: string };
trafficData: { completeness: number; quality: number; lastUpdate: string };
}> {
const response = await apiClient.get<ApiResponse<any>>('/api/v1/data/quality');
return response.data!;
const response = await apiClient.post<ApiResponse<UploadResponse>>(
'/api/v1/data/sales/import',
importData
);
return response.data!;
} catch (error: any) {
throw error;
}
}
/**
@@ -233,4 +298,5 @@ export class DataService {
}
}
// CRITICAL: Export the instance with the name expected by the index file
export const dataService = new DataService();