2025-08-03 17:48:34 +02:00
|
|
|
// frontend/src/api/hooks/useForecast.ts
|
|
|
|
|
/**
|
|
|
|
|
* Forecasting Operations Hooks
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { useState, useCallback } from 'react';
|
|
|
|
|
import { forecastingService } from '../services';
|
|
|
|
|
import type {
|
|
|
|
|
SingleForecastRequest,
|
|
|
|
|
BatchForecastRequest,
|
|
|
|
|
ForecastResponse,
|
|
|
|
|
BatchForecastResponse,
|
|
|
|
|
ForecastAlert,
|
|
|
|
|
QuickForecast,
|
|
|
|
|
} from '../types';
|
|
|
|
|
|
|
|
|
|
export const useForecast = () => {
|
|
|
|
|
const [forecasts, setForecasts] = useState<ForecastResponse[]>([]);
|
|
|
|
|
const [batchForecasts, setBatchForecasts] = useState<BatchForecastResponse[]>([]);
|
|
|
|
|
const [quickForecasts, setQuickForecasts] = useState<QuickForecast[]>([]);
|
|
|
|
|
const [alerts, setAlerts] = useState<ForecastAlert[]>([]);
|
|
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
|
|
|
|
|
|
const createSingleForecast = useCallback(async (
|
|
|
|
|
tenantId: string,
|
|
|
|
|
request: SingleForecastRequest
|
|
|
|
|
): Promise<ForecastResponse[]> => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
|
|
|
|
|
const newForecasts = await forecastingService.createSingleForecast(tenantId, request);
|
2025-08-15 17:53:59 +02:00
|
|
|
setForecasts(prev => [...newForecasts, ...(prev || [])]);
|
2025-08-03 17:48:34 +02:00
|
|
|
|
|
|
|
|
return newForecasts;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to create forecast';
|
|
|
|
|
setError(message);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const createBatchForecast = useCallback(async (
|
|
|
|
|
tenantId: string,
|
|
|
|
|
request: BatchForecastRequest
|
|
|
|
|
): Promise<BatchForecastResponse> => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
|
|
|
|
|
const batchForecast = await forecastingService.createBatchForecast(tenantId, request);
|
2025-08-15 17:53:59 +02:00
|
|
|
setBatchForecasts(prev => [batchForecast, ...(prev || [])]);
|
2025-08-03 17:48:34 +02:00
|
|
|
|
|
|
|
|
return batchForecast;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to create batch forecast';
|
|
|
|
|
setError(message);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const getForecasts = useCallback(async (tenantId: string): Promise<ForecastResponse[]> => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
|
|
|
|
|
const response = await forecastingService.getForecasts(tenantId);
|
|
|
|
|
setForecasts(response.data);
|
|
|
|
|
|
|
|
|
|
return response.data;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to get forecasts';
|
|
|
|
|
setError(message);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const getBatchForecastStatus = useCallback(async (
|
|
|
|
|
tenantId: string,
|
|
|
|
|
batchId: string
|
|
|
|
|
): Promise<BatchForecastResponse> => {
|
|
|
|
|
try {
|
|
|
|
|
const batchForecast = await forecastingService.getBatchForecastStatus(tenantId, batchId);
|
|
|
|
|
|
|
|
|
|
// Update batch forecast in state
|
2025-08-15 17:53:59 +02:00
|
|
|
setBatchForecasts(prev => (prev || []).map(bf =>
|
2025-08-03 17:48:34 +02:00
|
|
|
bf.id === batchId ? batchForecast : bf
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
return batchForecast;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to get batch forecast status';
|
|
|
|
|
setError(message);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const getQuickForecasts = useCallback(async (tenantId: string): Promise<QuickForecast[]> => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
|
|
|
|
|
const quickForecastData = await forecastingService.getQuickForecasts(tenantId);
|
|
|
|
|
setQuickForecasts(quickForecastData);
|
|
|
|
|
|
|
|
|
|
return quickForecastData;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to get quick forecasts';
|
|
|
|
|
setError(message);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
2025-08-15 23:11:53 +02:00
|
|
|
const getForecastAlerts = useCallback(async (tenantId: string): Promise<any> => {
|
2025-08-03 17:48:34 +02:00
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
|
|
|
|
|
const response = await forecastingService.getForecastAlerts(tenantId);
|
|
|
|
|
|
2025-08-15 23:11:53 +02:00
|
|
|
// Handle different response formats
|
2025-08-21 20:28:14 +02:00
|
|
|
if (response && 'data' in response && response.data) {
|
|
|
|
|
// Standard paginated format: { data: [...], pagination: {...} }
|
2025-08-15 23:11:53 +02:00
|
|
|
setAlerts(response.data);
|
2025-08-21 20:28:14 +02:00
|
|
|
return { alerts: response.data, ...response };
|
|
|
|
|
} else if (response && Array.isArray(response)) {
|
|
|
|
|
// Direct array format
|
|
|
|
|
setAlerts(response);
|
|
|
|
|
return { alerts: response };
|
2025-08-15 23:11:53 +02:00
|
|
|
} else if (Array.isArray(response)) {
|
|
|
|
|
// Direct array format
|
|
|
|
|
setAlerts(response);
|
|
|
|
|
return { alerts: response };
|
|
|
|
|
} else {
|
|
|
|
|
// Unknown format - return empty
|
|
|
|
|
setAlerts([]);
|
|
|
|
|
return { alerts: [] };
|
|
|
|
|
}
|
2025-08-03 17:48:34 +02:00
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to get forecast alerts';
|
|
|
|
|
setError(message);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const acknowledgeForecastAlert = useCallback(async (
|
|
|
|
|
tenantId: string,
|
|
|
|
|
alertId: string
|
|
|
|
|
): Promise<void> => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
|
|
|
|
|
const acknowledgedAlert = await forecastingService.acknowledgeForecastAlert(tenantId, alertId);
|
2025-08-15 17:53:59 +02:00
|
|
|
setAlerts(prev => (prev || []).map(alert =>
|
2025-08-03 17:48:34 +02:00
|
|
|
alert.id === alertId ? acknowledgedAlert : alert
|
|
|
|
|
));
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to acknowledge alert';
|
|
|
|
|
setError(message);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const exportForecasts = useCallback(async (
|
|
|
|
|
tenantId: string,
|
|
|
|
|
format: 'csv' | 'excel' | 'json',
|
|
|
|
|
params?: {
|
2025-08-14 16:47:34 +02:00
|
|
|
inventory_product_id?: string; // Primary way to filter by product
|
|
|
|
|
product_name?: string; // For backward compatibility
|
2025-08-03 17:48:34 +02:00
|
|
|
start_date?: string;
|
|
|
|
|
end_date?: string;
|
|
|
|
|
}
|
|
|
|
|
): Promise<void> => {
|
|
|
|
|
try {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
|
|
|
|
|
const blob = await forecastingService.exportForecasts(tenantId, format, params);
|
|
|
|
|
|
|
|
|
|
// Create download link
|
|
|
|
|
const url = window.URL.createObjectURL(blob);
|
|
|
|
|
const link = document.createElement('a');
|
|
|
|
|
link.href = url;
|
|
|
|
|
link.download = `forecasts.${format}`;
|
|
|
|
|
document.body.appendChild(link);
|
|
|
|
|
link.click();
|
|
|
|
|
document.body.removeChild(link);
|
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Export failed';
|
|
|
|
|
setError(message);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
forecasts,
|
|
|
|
|
batchForecasts,
|
|
|
|
|
quickForecasts,
|
|
|
|
|
alerts,
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
|
|
|
|
createSingleForecast,
|
|
|
|
|
createBatchForecast,
|
|
|
|
|
getForecasts,
|
|
|
|
|
getBatchForecastStatus,
|
|
|
|
|
getQuickForecasts,
|
|
|
|
|
getForecastAlerts,
|
|
|
|
|
acknowledgeForecastAlert,
|
|
|
|
|
exportForecasts,
|
|
|
|
|
clearError: () => setError(null),
|
|
|
|
|
};
|
|
|
|
|
};
|