Files
bakery-ia/frontend/src/api/services/forecasting.service.ts

226 lines
6.0 KiB
TypeScript
Raw Normal View History

2025-08-03 17:48:34 +02:00
// frontend/src/api/services/forecasting.service.ts
/**
* Forecasting Service
* Handles forecast operations and predictions
*/
import { apiClient } from '../client';
import { RequestTimeouts } from '../client/config';
import type {
SingleForecastRequest,
BatchForecastRequest,
ForecastResponse,
BatchForecastResponse,
ForecastAlert,
QuickForecast,
PaginatedResponse,
BaseQueryParams,
} from '../types';
export class ForecastingService {
/**
* Create Single Product Forecast
*/
async createSingleForecast(
2025-08-04 22:46:05 +02:00
tenantId: string,
request: SingleForecastRequest
): Promise<ForecastResponse[]> {
console.log('🔮 Creating single forecast:', { tenantId, request });
try {
// Backend returns single ForecastResponse object
const response = await apiClient.post(
`/tenants/${tenantId}/forecasts/single`,
request,
{
timeout: RequestTimeouts.MEDIUM,
}
);
console.log('🔮 Forecast API Response:', response);
console.log('- Type:', typeof response);
console.log('- Is Array:', Array.isArray(response));
// ✅ FIX: Convert single response to array
if (response && typeof response === 'object' && !Array.isArray(response)) {
// Single forecast response - wrap in array
const forecastArray = [response as ForecastResponse];
console.log('✅ Converted single forecast to array:', forecastArray);
return forecastArray;
} else if (Array.isArray(response)) {
// Already an array (unexpected but handle gracefully)
console.log('✅ Response is already an array:', response);
return response;
} else {
console.error('❌ Unexpected response format:', response);
throw new Error('Invalid forecast response format');
}
} catch (error) {
console.error('❌ Forecast API Error:', error);
throw error;
2025-08-03 17:48:34 +02:00
}
}
/**
* Create Batch Forecast
*/
async createBatchForecast(
tenantId: string,
request: BatchForecastRequest
): Promise<BatchForecastResponse> {
return apiClient.post(
`/tenants/${tenantId}/forecasts/batch`,
request,
{
timeout: RequestTimeouts.LONG,
}
);
}
/**
* Get Forecast by ID
*/
async getForecast(tenantId: string, forecastId: string): Promise<ForecastResponse> {
return apiClient.get(`/tenants/${tenantId}/forecasts/${forecastId}`);
}
/**
* Get Forecasts
*/
async getForecasts(
tenantId: string,
params?: BaseQueryParams & {
product_name?: string;
start_date?: string;
end_date?: string;
model_id?: string;
}
): Promise<PaginatedResponse<ForecastResponse>> {
return apiClient.get(`/tenants/${tenantId}/forecasts`, { params });
}
/**
* Get Batch Forecast Status
*/
async getBatchForecastStatus(
tenantId: string,
batchId: string
): Promise<BatchForecastResponse> {
return apiClient.get(`/tenants/${tenantId}/forecasts/batch/${batchId}/status`);
}
/**
* Get Batch Forecasts
*/
async getBatchForecasts(
tenantId: string,
params?: BaseQueryParams & {
status?: string;
start_date?: string;
end_date?: string;
}
): Promise<PaginatedResponse<BatchForecastResponse>> {
return apiClient.get(`/tenants/${tenantId}/forecasts/batch`, { params });
}
/**
* Cancel Batch Forecast
*/
async cancelBatchForecast(tenantId: string, batchId: string): Promise<{ message: string }> {
return apiClient.post(`/tenants/${tenantId}/forecasts/batch/${batchId}/cancel`);
}
/**
* Get Quick Forecasts for Dashboard
*/
async getQuickForecasts(tenantId: string, limit?: number): Promise<QuickForecast[]> {
return apiClient.get(`/tenants/${tenantId}/forecasts/quick`, {
params: { limit },
});
}
/**
* Get Forecast Alerts
*/
async getForecastAlerts(
tenantId: string,
params?: BaseQueryParams & {
is_active?: boolean;
severity?: string;
alert_type?: string;
}
): Promise<PaginatedResponse<ForecastAlert>> {
return apiClient.get(`/tenants/${tenantId}/forecasts/alerts`, { params });
}
/**
* Acknowledge Forecast Alert
*/
async acknowledgeForecastAlert(
tenantId: string,
alertId: string
): Promise<ForecastAlert> {
return apiClient.post(`/tenants/${tenantId}/forecasts/alerts/${alertId}/acknowledge`);
}
/**
* Delete Forecast
*/
async deleteForecast(tenantId: string, forecastId: string): Promise<{ message: string }> {
return apiClient.delete(`/tenants/${tenantId}/forecasts/${forecastId}`);
}
/**
* Export Forecasts
*/
async exportForecasts(
tenantId: string,
format: 'csv' | 'excel' | 'json',
params?: {
product_name?: string;
start_date?: string;
end_date?: string;
}
): Promise<Blob> {
const response = await apiClient.request(`/tenants/${tenantId}/forecasts/export`, {
method: 'GET',
params: { ...params, format },
headers: {
'Accept': format === 'csv' ? 'text/csv' :
format === 'excel' ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' :
'application/json',
},
});
return new Blob([response], {
type: format === 'csv' ? 'text/csv' :
format === 'excel' ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' :
'application/json',
});
}
/**
* Get Forecast Accuracy Metrics
*/
async getForecastAccuracy(
tenantId: string,
params?: {
product_name?: string;
model_id?: string;
start_date?: string;
end_date?: string;
}
): Promise<{
overall_accuracy: number;
product_accuracy: Array<{
product_name: string;
accuracy: number;
sample_size: number;
}>;
}> {
return apiClient.get(`/tenants/${tenantId}/forecasts/accuracy`, { params });
}
}
export const forecastingService = new ForecastingService();