REFACTOR data service

This commit is contained in:
Urtzi Alfaro
2025-08-12 18:17:30 +02:00
parent 7c237c0acc
commit fbe7470ad9
149 changed files with 8528 additions and 7393 deletions

View File

@@ -24,7 +24,7 @@ export class AuthService {
/**
* User Registration
*/
async register(data: RegisterRequest): Promise<{ user: UserResponse }> {
async register(data: RegisterRequest): Promise<LoginResponse> {
return apiClient.post(`${this.baseEndpoint}/register`, data);
}

View File

@@ -0,0 +1,264 @@
// frontend/src/api/services/external.service.ts
/**
* External Data Service
* Handles weather and traffic data operations for the external microservice
*/
import { apiClient } from '../client';
import { RequestTimeouts } from '../client/config';
// Align with backend WeatherDataResponse schema
export interface WeatherData {
date: string;
temperature?: number;
precipitation?: number;
humidity?: number;
wind_speed?: number;
pressure?: number;
description?: string;
source: string;
}
// Align with backend TrafficDataResponse schema
export interface TrafficData {
date: string;
traffic_volume?: number;
pedestrian_count?: number;
congestion_level?: string;
average_speed?: number;
source: string;
}
export interface WeatherForecast {
date: string;
temperature_min: number;
temperature_max: number;
temperature_avg: number;
precipitation: number;
description: string;
humidity?: number;
wind_speed?: number;
}
export class ExternalService {
/**
* Get Current Weather Data
*/
async getCurrentWeather(
tenantId: string,
lat: number,
lon: number
): Promise<WeatherData> {
try {
// ✅ FIX 1: Correct endpoint path with tenant ID
const endpoint = `/tenants/${tenantId}/weather/current`;
// ✅ FIX 2: Correct parameter names (latitude/longitude, not lat/lon)
const response = await apiClient.get(endpoint, {
params: {
latitude: lat, // Backend expects 'latitude'
longitude: lon // Backend expects 'longitude'
}
});
console.log('Weather API response:', response);
// Return backend response directly (matches WeatherData interface)
return response;
} catch (error) {
console.error('Failed to fetch weather from backend:', error);
// Fallback weather for Madrid (matching WeatherData schema)
return {
date: new Date().toISOString(),
temperature: 18,
description: 'Parcialmente nublado',
precipitation: 0,
humidity: 65,
wind_speed: 10,
source: 'fallback'
};
}
}
/**
* Get Weather Forecast
*/
async getWeatherForecast(
tenantId: string,
lat: number,
lon: number,
days: number = 7
): Promise<WeatherForecast[]> {
try {
// Fix: Use POST with JSON body as expected by backend
const response = await apiClient.post(`/tenants/${tenantId}/weather/forecast`, {
latitude: lat,
longitude: lon,
days: days
});
// Handle response format
if (Array.isArray(response)) {
return response;
} else if (response && response.forecasts) {
return response.forecasts;
} else {
console.warn('Unexpected weather forecast response format:', response);
return [];
}
} catch (error) {
console.error('Failed to fetch weather forecast:', error);
return [];
}
}
/**
* Get Historical Weather Data
*/
async getHistoricalWeather(
tenantId: string,
lat: number,
lon: number,
startDate: string,
endDate: string
): Promise<WeatherData[]> {
try {
// Fix: Use POST with JSON body as expected by backend
const response = await apiClient.post(`/tenants/${tenantId}/weather/historical`, {
latitude: lat,
longitude: lon,
start_date: startDate,
end_date: endDate
});
// Return backend response directly (matches WeatherData interface)
return Array.isArray(response) ? response : response.data || [];
} catch (error) {
console.error('Failed to fetch historical weather:', error);
return [];
}
}
/**
* Get Current Traffic Data
*/
async getCurrentTraffic(
tenantId: string,
lat: number,
lon: number
): Promise<TrafficData> {
try {
const response = await apiClient.get(`/tenants/${tenantId}/traffic/current`, {
params: {
latitude: lat,
longitude: lon
}
});
// Return backend response directly (matches TrafficData interface)
return response;
} catch (error) {
console.error('Failed to fetch traffic data:', error);
// Fallback traffic data (matching TrafficData schema)
return {
date: new Date().toISOString(),
traffic_volume: 50,
pedestrian_count: 25,
congestion_level: 'medium',
average_speed: 30,
source: 'fallback'
};
}
}
/**
* Get Traffic Forecast
*/
async getTrafficForecast(
tenantId: string,
lat: number,
lon: number,
hours: number = 24
): Promise<TrafficData[]> {
try {
// Fix: Use POST with JSON body as expected by backend
const response = await apiClient.post(`/tenants/${tenantId}/traffic/forecast`, {
latitude: lat,
longitude: lon,
hours: hours
});
// Return backend response directly (matches TrafficData interface)
return Array.isArray(response) ? response : response.data || [];
} catch (error) {
console.error('Failed to fetch traffic forecast:', error);
return [];
}
}
/**
* Get Historical Traffic Data
*/
async getHistoricalTraffic(
tenantId: string,
lat: number,
lon: number,
startDate: string,
endDate: string
): Promise<TrafficData[]> {
try {
// Fix: Use POST with JSON body as expected by backend
const response = await apiClient.post(`/tenants/${tenantId}/traffic/historical`, {
latitude: lat,
longitude: lon,
start_date: startDate,
end_date: endDate
});
// Return backend response directly (matches TrafficData interface)
return Array.isArray(response) ? response : response.data || [];
} catch (error) {
console.error('Failed to fetch historical traffic:', error);
return [];
}
}
/**
* Test External Service Connectivity
*/
async testConnectivity(tenantId: string): Promise<{
weather: boolean;
traffic: boolean;
overall: boolean;
}> {
const results = {
weather: false,
traffic: false,
overall: false
};
try {
// Test weather service
await this.getCurrentWeather(tenantId, 40.4168, -3.7038); // Madrid coordinates
results.weather = true;
} catch (error) {
console.warn('Weather service connectivity test failed:', error);
}
try {
// Test traffic service
await this.getCurrentTraffic(tenantId, 40.4168, -3.7038); // Madrid coordinates
results.traffic = true;
} catch (error) {
console.warn('Traffic service connectivity test failed:', error);
}
results.overall = results.weather && results.traffic;
return results;
}
}
export const externalService = new ExternalService();

View File

@@ -7,7 +7,8 @@
// Import and export individual services
import { AuthService } from './auth.service';
import { TenantService } from './tenant.service';
import { DataService } from './data.service';
import { SalesService } from './sales.service';
import { ExternalService } from './external.service';
import { TrainingService } from './training.service';
import { ForecastingService } from './forecasting.service';
import { NotificationService } from './notification.service';
@@ -16,17 +17,28 @@ import { OnboardingService } from './onboarding.service';
// Create service instances
export const authService = new AuthService();
export const tenantService = new TenantService();
export const dataService = new DataService();
export const salesService = new SalesService();
export const externalService = new ExternalService();
export const trainingService = new TrainingService();
export const forecastingService = new ForecastingService();
export const notificationService = new NotificationService();
export const onboardingService = new OnboardingService();
// Export the classes as well
export { AuthService, TenantService, DataService, TrainingService, ForecastingService, NotificationService, OnboardingService };
export {
AuthService,
TenantService,
SalesService,
ExternalService,
TrainingService,
ForecastingService,
NotificationService,
OnboardingService
};
// Import base client
export { apiClient } from '../client';
import { apiClient } from '../client';
export { apiClient };
// Re-export all types
export * from '../types';
@@ -35,7 +47,8 @@ export * from '../types';
export const api = {
auth: authService,
tenant: tenantService,
data: dataService,
sales: salesService,
external: externalService,
training: trainingService,
forecasting: forecastingService,
notification: notificationService,
@@ -56,7 +69,8 @@ export class HealthService {
const services = [
{ name: 'Auth', endpoint: '/auth/health' },
{ name: 'Tenant', endpoint: '/tenants/health' },
{ name: 'Data', endpoint: '/data/health' },
{ name: 'Sales', endpoint: '/sales/health' },
{ name: 'External', endpoint: '/external/health' },
{ name: 'Training', endpoint: '/training/health' },
{ name: 'Forecasting', endpoint: '/forecasting/health' },
{ name: 'Notification', endpoint: '/notifications/health' },

View File

@@ -1,7 +1,7 @@
// frontend/src/api/services/data.service.ts
// frontend/src/api/services/sales.service.ts
/**
* Data Management Service
* Handles sales data operations
* Sales Data Service
* Handles sales data operations for the sales microservice
*/
import { apiClient } from '../client';
@@ -17,7 +17,7 @@ import type {
ActivityItem,
} from '../types';
export class DataService {
export class SalesService {
/**
* Upload Sales History File
*/
@@ -143,7 +143,7 @@ export class DataService {
metrics?: string[];
}
): Promise<any> {
return apiClient.get(`/tenants/${tenantId}/analytics`, { params });
return apiClient.get(`/tenants/${tenantId}/sales/analytics`, { params });
}
/**
@@ -175,14 +175,13 @@ export class DataService {
* Get Recent Activity
*/
async getRecentActivity(tenantId: string, limit?: number): Promise<ActivityItem[]> {
return apiClient.get(`/tenants/${tenantId}/activity`, {
return apiClient.get(`/tenants/${tenantId}/sales/activity`, {
params: { limit },
});
}
/**
* Get Products List from Sales Data
* This should be added to the DataService class
*/
async getProductsList(tenantId: string): Promise<string[]> {
try {
@@ -261,90 +260,8 @@ export class DataService {
}
}
/**
* Get Current Weather Data
* This should be added to the DataService class
*/
async getCurrentWeather(
tenantId: string,
lat: number,
lon: number
): Promise<{
temperature: number;
description: string;
precipitation: number;
humidity?: number;
wind_speed?: number;
}> {
try {
// ✅ FIX 1: Correct endpoint path with tenant ID
const endpoint = `/tenants/${tenantId}/weather/current`;
// ✅ FIX 2: Correct parameter names (latitude/longitude, not lat/lon)
const response = await apiClient.get(endpoint, {
params: {
latitude: lat, // Backend expects 'latitude'
longitude: lon // Backend expects 'longitude'
}
});
// ✅ FIX 3: Handle the actual backend response structure
// Backend returns WeatherDataResponse:
// {
// "date": "2025-08-04T12:00:00Z",
// "temperature": 25.5,
// "precipitation": 0.0,
// "humidity": 65.0,
// "wind_speed": 10.2,
// "pressure": 1013.2,
// "description": "Partly cloudy",
// "source": "aemet"
// }
console.log('Weather API response:', response);
// Map backend response to expected frontend format
return {
temperature: response.temperature || 18,
description: response.description || 'Parcialmente nublado',
precipitation: response.precipitation || 0,
humidity: response.humidity || 65,
wind_speed: response.wind_speed || 10
};
} catch (error) {
console.error('Failed to fetch weather from backend:', error);
// Fallback weather for Madrid
return {
temperature: 18,
description: 'Parcialmente nublado',
precipitation: 0,
humidity: 65,
wind_speed: 10
};
}
}
/**
* Get Weather Forecast
* This should be added to the DataService class
*/
async getWeatherForecast(
lat: number,
lon: number,
days: number = 7
): Promise<any[]> {
return apiClient.get(`/data/weather/forecast`, {
params: { lat, lon, days }
});
}
/**
* Get Sales Summary by Period
* This should be added to the DataService class
*/
async getSalesSummary(
tenantId: string,
@@ -357,7 +274,6 @@ export class DataService {
/**
* Get Sales Analytics
* This should be added to the DataService class
*/
async getSalesAnalytics(
tenantId: string,
@@ -369,14 +285,13 @@ export class DataService {
forecast_accuracy?: number;
stockout_events?: number;
}> {
return apiClient.get(`/tenants/${tenantId}/sales/analytics`, {
return apiClient.get(`/tenants/${tenantId}/sales/analytics/summary`, {
params: {
start_date: startDate,
end_date: endDate
}
});
}
}
export const dataService = new DataService();
export const salesService = new SalesService();