Add new frontend - fix 22
This commit is contained in:
@@ -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();
|
||||
Reference in New Issue
Block a user