Add frontend imporvements
This commit is contained in:
323
frontend/src/api/hooks/forecasting.ts
Normal file
323
frontend/src/api/hooks/forecasting.ts
Normal file
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* Forecasting React Query hooks
|
||||
*/
|
||||
import {
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
UseQueryOptions,
|
||||
UseMutationOptions,
|
||||
useInfiniteQuery,
|
||||
UseInfiniteQueryOptions,
|
||||
} from '@tanstack/react-query';
|
||||
import { forecastingService } from '../services/forecasting';
|
||||
import {
|
||||
ForecastRequest,
|
||||
ForecastResponse,
|
||||
BatchForecastRequest,
|
||||
BatchForecastResponse,
|
||||
ForecastListResponse,
|
||||
ForecastByIdResponse,
|
||||
ForecastStatistics,
|
||||
DeleteForecastResponse,
|
||||
GetForecastsParams,
|
||||
ForecastingHealthResponse,
|
||||
} from '../types/forecasting';
|
||||
import { ApiError } from '../client/apiClient';
|
||||
|
||||
// ================================================================
|
||||
// QUERY KEYS
|
||||
// ================================================================
|
||||
|
||||
export const forecastingKeys = {
|
||||
all: ['forecasting'] as const,
|
||||
lists: () => [...forecastingKeys.all, 'list'] as const,
|
||||
list: (tenantId: string, filters?: GetForecastsParams) =>
|
||||
[...forecastingKeys.lists(), tenantId, filters] as const,
|
||||
details: () => [...forecastingKeys.all, 'detail'] as const,
|
||||
detail: (tenantId: string, forecastId: string) =>
|
||||
[...forecastingKeys.details(), tenantId, forecastId] as const,
|
||||
statistics: (tenantId: string) =>
|
||||
[...forecastingKeys.all, 'statistics', tenantId] as const,
|
||||
health: () => [...forecastingKeys.all, 'health'] as const,
|
||||
} as const;
|
||||
|
||||
// ================================================================
|
||||
// QUERIES
|
||||
// ================================================================
|
||||
|
||||
/**
|
||||
* Get tenant forecasts with filtering and pagination
|
||||
*/
|
||||
export const useTenantForecasts = (
|
||||
tenantId: string,
|
||||
params?: GetForecastsParams,
|
||||
options?: Omit<UseQueryOptions<ForecastListResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ForecastListResponse, ApiError>({
|
||||
queryKey: forecastingKeys.list(tenantId, params),
|
||||
queryFn: () => forecastingService.getTenantForecasts(tenantId, params),
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
enabled: !!tenantId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get specific forecast by ID
|
||||
*/
|
||||
export const useForecastById = (
|
||||
tenantId: string,
|
||||
forecastId: string,
|
||||
options?: Omit<UseQueryOptions<ForecastByIdResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ForecastByIdResponse, ApiError>({
|
||||
queryKey: forecastingKeys.detail(tenantId, forecastId),
|
||||
queryFn: () => forecastingService.getForecastById(tenantId, forecastId),
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
enabled: !!tenantId && !!forecastId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get forecast statistics for tenant
|
||||
*/
|
||||
export const useForecastStatistics = (
|
||||
tenantId: string,
|
||||
options?: Omit<UseQueryOptions<ForecastStatistics, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ForecastStatistics, ApiError>({
|
||||
queryKey: forecastingKeys.statistics(tenantId),
|
||||
queryFn: () => forecastingService.getForecastStatistics(tenantId),
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
enabled: !!tenantId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Health check for forecasting service
|
||||
*/
|
||||
export const useForecastingHealth = (
|
||||
options?: Omit<UseQueryOptions<ForecastingHealthResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<ForecastingHealthResponse, ApiError>({
|
||||
queryKey: forecastingKeys.health(),
|
||||
queryFn: () => forecastingService.getHealthCheck(),
|
||||
staleTime: 30 * 1000, // 30 seconds
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// ================================================================
|
||||
// INFINITE QUERIES
|
||||
// ================================================================
|
||||
|
||||
/**
|
||||
* Infinite query for tenant forecasts (for pagination)
|
||||
*/
|
||||
export const useInfiniteTenantForecasts = (
|
||||
tenantId: string,
|
||||
baseParams?: Omit<GetForecastsParams, 'skip' | 'limit'>,
|
||||
options?: Omit<UseInfiniteQueryOptions<ForecastListResponse, ApiError>, 'queryKey' | 'queryFn' | 'getNextPageParam'>
|
||||
) => {
|
||||
const limit = baseParams?.limit || 20;
|
||||
|
||||
return useInfiniteQuery<ForecastListResponse, ApiError>({
|
||||
queryKey: [...forecastingKeys.list(tenantId, baseParams), 'infinite'],
|
||||
queryFn: ({ pageParam = 0 }) => {
|
||||
const params: GetForecastsParams = {
|
||||
...baseParams,
|
||||
skip: pageParam as number,
|
||||
limit,
|
||||
};
|
||||
return forecastingService.getTenantForecasts(tenantId, params);
|
||||
},
|
||||
getNextPageParam: (lastPage, allPages) => {
|
||||
const totalFetched = allPages.reduce((sum, page) => sum + page.total_returned, 0);
|
||||
return lastPage.total_returned === limit ? totalFetched : undefined;
|
||||
},
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
enabled: !!tenantId,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// ================================================================
|
||||
// MUTATIONS
|
||||
// ================================================================
|
||||
|
||||
/**
|
||||
* Create single forecast mutation
|
||||
*/
|
||||
export const useCreateSingleForecast = (
|
||||
options?: UseMutationOptions<
|
||||
ForecastResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; request: ForecastRequest }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
ForecastResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; request: ForecastRequest }
|
||||
>({
|
||||
mutationFn: ({ tenantId, request }) =>
|
||||
forecastingService.createSingleForecast(tenantId, request),
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate and refetch forecasts list
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.lists(),
|
||||
});
|
||||
|
||||
// Update the specific forecast cache
|
||||
queryClient.setQueryData(
|
||||
forecastingKeys.detail(variables.tenantId, data.id),
|
||||
{
|
||||
...data,
|
||||
enhanced_features: true,
|
||||
repository_integration: true,
|
||||
} as ForecastByIdResponse
|
||||
);
|
||||
|
||||
// Update statistics
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.statistics(variables.tenantId),
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create batch forecast mutation
|
||||
*/
|
||||
export const useCreateBatchForecast = (
|
||||
options?: UseMutationOptions<
|
||||
BatchForecastResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; request: BatchForecastRequest }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
BatchForecastResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; request: BatchForecastRequest }
|
||||
>({
|
||||
mutationFn: ({ tenantId, request }) =>
|
||||
forecastingService.createBatchForecast(tenantId, request),
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate forecasts list
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.lists(),
|
||||
});
|
||||
|
||||
// Cache individual forecasts if available
|
||||
if (data.forecasts) {
|
||||
data.forecasts.forEach((forecast) => {
|
||||
queryClient.setQueryData(
|
||||
forecastingKeys.detail(variables.tenantId, forecast.id),
|
||||
{
|
||||
...forecast,
|
||||
enhanced_features: true,
|
||||
repository_integration: true,
|
||||
} as ForecastByIdResponse
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Update statistics
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.statistics(variables.tenantId),
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete forecast mutation
|
||||
*/
|
||||
export const useDeleteForecast = (
|
||||
options?: UseMutationOptions<
|
||||
DeleteForecastResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; forecastId: string }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
DeleteForecastResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; forecastId: string }
|
||||
>({
|
||||
mutationFn: ({ tenantId, forecastId }) =>
|
||||
forecastingService.deleteForecast(tenantId, forecastId),
|
||||
onSuccess: (data, variables) => {
|
||||
// Remove from cache
|
||||
queryClient.removeQueries({
|
||||
queryKey: forecastingKeys.detail(variables.tenantId, variables.forecastId),
|
||||
});
|
||||
|
||||
// Invalidate lists to refresh
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.lists(),
|
||||
});
|
||||
|
||||
// Update statistics
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.statistics(variables.tenantId),
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// ================================================================
|
||||
// UTILITY FUNCTIONS
|
||||
// ================================================================
|
||||
|
||||
/**
|
||||
* Prefetch forecast by ID
|
||||
*/
|
||||
export const usePrefetchForecast = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return (tenantId: string, forecastId: string) => {
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: forecastingKeys.detail(tenantId, forecastId),
|
||||
queryFn: () => forecastingService.getForecastById(tenantId, forecastId),
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Invalidate all forecasting queries for a tenant
|
||||
*/
|
||||
export const useInvalidateForecasting = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return (tenantId?: string) => {
|
||||
if (tenantId) {
|
||||
// Invalidate specific tenant queries
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.list(tenantId),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.statistics(tenantId),
|
||||
});
|
||||
} else {
|
||||
// Invalidate all forecasting queries
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: forecastingKeys.all,
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -25,6 +25,7 @@ export { trainingService } from './services/training';
|
||||
export { alertProcessorService } from './services/alert_processor';
|
||||
export { suppliersService } from './services/suppliers';
|
||||
export { OrdersService } from './services/orders';
|
||||
export { forecastingService } from './services/forecasting';
|
||||
|
||||
// Types - Auth
|
||||
export type {
|
||||
@@ -295,6 +296,22 @@ export type {
|
||||
UpdatePlanStatusParams,
|
||||
} from './types/orders';
|
||||
|
||||
// Types - Forecasting
|
||||
export type {
|
||||
ForecastRequest,
|
||||
ForecastResponse,
|
||||
BatchForecastRequest,
|
||||
BatchForecastResponse,
|
||||
ForecastStatistics,
|
||||
ForecastListResponse,
|
||||
ForecastByIdResponse,
|
||||
DeleteForecastResponse,
|
||||
GetForecastsParams,
|
||||
ForecastingHealthResponse,
|
||||
} from './types/forecasting';
|
||||
|
||||
export { BusinessType } from './types/forecasting';
|
||||
|
||||
// Hooks - Auth
|
||||
export {
|
||||
useAuthProfile,
|
||||
@@ -551,6 +568,21 @@ export {
|
||||
ordersKeys,
|
||||
} from './hooks/orders';
|
||||
|
||||
// Hooks - Forecasting
|
||||
export {
|
||||
useTenantForecasts,
|
||||
useForecastById,
|
||||
useForecastStatistics,
|
||||
useForecastingHealth,
|
||||
useInfiniteTenantForecasts,
|
||||
useCreateSingleForecast,
|
||||
useCreateBatchForecast,
|
||||
useDeleteForecast,
|
||||
usePrefetchForecast,
|
||||
useInvalidateForecasting,
|
||||
forecastingKeys,
|
||||
} from './hooks/forecasting';
|
||||
|
||||
// Query Key Factories (for advanced usage)
|
||||
export {
|
||||
authKeys,
|
||||
@@ -567,4 +599,5 @@ export {
|
||||
suppliersKeys,
|
||||
ordersKeys,
|
||||
dataImportKeys,
|
||||
forecastingKeys,
|
||||
};
|
||||
132
frontend/src/api/services/forecasting.ts
Normal file
132
frontend/src/api/services/forecasting.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Forecasting Service
|
||||
* API calls for forecasting service endpoints
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client/apiClient';
|
||||
import {
|
||||
ForecastRequest,
|
||||
ForecastResponse,
|
||||
BatchForecastRequest,
|
||||
BatchForecastResponse,
|
||||
ForecastListResponse,
|
||||
ForecastByIdResponse,
|
||||
ForecastStatistics,
|
||||
DeleteForecastResponse,
|
||||
GetForecastsParams,
|
||||
ForecastingHealthResponse,
|
||||
} from '../types/forecasting';
|
||||
|
||||
export class ForecastingService {
|
||||
private readonly baseUrl = '/forecasts';
|
||||
|
||||
/**
|
||||
* Generate a single product forecast
|
||||
* POST /tenants/{tenant_id}/forecasts/single
|
||||
*/
|
||||
async createSingleForecast(
|
||||
tenantId: string,
|
||||
request: ForecastRequest
|
||||
): Promise<ForecastResponse> {
|
||||
return apiClient.post<ForecastResponse, ForecastRequest>(
|
||||
`/tenants/${tenantId}${this.baseUrl}/single`,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate batch forecasts for multiple products
|
||||
* POST /tenants/{tenant_id}/forecasts/batch
|
||||
*/
|
||||
async createBatchForecast(
|
||||
tenantId: string,
|
||||
request: BatchForecastRequest
|
||||
): Promise<BatchForecastResponse> {
|
||||
return apiClient.post<BatchForecastResponse, BatchForecastRequest>(
|
||||
`/tenants/${tenantId}${this.baseUrl}/batch`,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tenant forecasts with filtering and pagination
|
||||
* GET /tenants/{tenant_id}/forecasts
|
||||
*/
|
||||
async getTenantForecasts(
|
||||
tenantId: string,
|
||||
params?: GetForecastsParams
|
||||
): Promise<ForecastListResponse> {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
if (params?.inventory_product_id) {
|
||||
searchParams.append('inventory_product_id', params.inventory_product_id);
|
||||
}
|
||||
if (params?.start_date) {
|
||||
searchParams.append('start_date', params.start_date);
|
||||
}
|
||||
if (params?.end_date) {
|
||||
searchParams.append('end_date', params.end_date);
|
||||
}
|
||||
if (params?.skip !== undefined) {
|
||||
searchParams.append('skip', params.skip.toString());
|
||||
}
|
||||
if (params?.limit !== undefined) {
|
||||
searchParams.append('limit', params.limit.toString());
|
||||
}
|
||||
|
||||
const queryString = searchParams.toString();
|
||||
const url = `/tenants/${tenantId}${this.baseUrl}${queryString ? `?${queryString}` : ''}`;
|
||||
|
||||
return apiClient.get<ForecastListResponse>(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific forecast by ID
|
||||
* GET /tenants/{tenant_id}/forecasts/{forecast_id}
|
||||
*/
|
||||
async getForecastById(
|
||||
tenantId: string,
|
||||
forecastId: string
|
||||
): Promise<ForecastByIdResponse> {
|
||||
return apiClient.get<ForecastByIdResponse>(
|
||||
`/tenants/${tenantId}${this.baseUrl}/${forecastId}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a forecast
|
||||
* DELETE /tenants/{tenant_id}/forecasts/{forecast_id}
|
||||
*/
|
||||
async deleteForecast(
|
||||
tenantId: string,
|
||||
forecastId: string
|
||||
): Promise<DeleteForecastResponse> {
|
||||
return apiClient.delete<DeleteForecastResponse>(
|
||||
`/tenants/${tenantId}${this.baseUrl}/${forecastId}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comprehensive forecast statistics
|
||||
* GET /tenants/{tenant_id}/forecasts/statistics
|
||||
*/
|
||||
async getForecastStatistics(
|
||||
tenantId: string
|
||||
): Promise<ForecastStatistics> {
|
||||
return apiClient.get<ForecastStatistics>(
|
||||
`/tenants/${tenantId}${this.baseUrl}/statistics`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check for forecasting service
|
||||
* GET /health
|
||||
*/
|
||||
async getHealthCheck(): Promise<ForecastingHealthResponse> {
|
||||
return apiClient.get<ForecastingHealthResponse>('/health');
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const forecastingService = new ForecastingService();
|
||||
export default forecastingService;
|
||||
@@ -83,7 +83,7 @@ export class InventoryService {
|
||||
}
|
||||
|
||||
async getLowStockIngredients(tenantId: string): Promise<IngredientResponse[]> {
|
||||
return apiClient.get<IngredientResponse[]>(`${this.baseUrl}/${tenantId}/ingredients/low-stock`);
|
||||
return apiClient.get<IngredientResponse[]>(`${this.baseUrl}/${tenantId}/stock/low-stock`);
|
||||
}
|
||||
|
||||
// Stock Management
|
||||
@@ -104,7 +104,7 @@ export class InventoryService {
|
||||
queryParams.append('include_unavailable', includeUnavailable.toString());
|
||||
|
||||
return apiClient.get<StockResponse[]>(
|
||||
`${this.baseUrl}/${tenantId}/stock/ingredient/${ingredientId}?${queryParams.toString()}`
|
||||
`${this.baseUrl}/${tenantId}/ingredients/${ingredientId}/stock?${queryParams.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -218,8 +218,8 @@ export class InventoryService {
|
||||
if (endDate) queryParams.append('end_date', endDate);
|
||||
|
||||
const url = queryParams.toString()
|
||||
? `${this.baseUrl}/${tenantId}/inventory/analytics?${queryParams.toString()}`
|
||||
: `${this.baseUrl}/${tenantId}/inventory/analytics`;
|
||||
? `${this.baseUrl}/${tenantId}/dashboard/analytics?${queryParams.toString()}`
|
||||
: `${this.baseUrl}/${tenantId}/dashboard/analytics`;
|
||||
|
||||
return apiClient.get(url);
|
||||
}
|
||||
|
||||
160
frontend/src/api/types/forecasting.ts
Normal file
160
frontend/src/api/types/forecasting.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Forecasting API Types
|
||||
* Mirror of backend forecasting service schemas
|
||||
*/
|
||||
|
||||
// ================================================================
|
||||
// ENUMS
|
||||
// ================================================================
|
||||
|
||||
export enum BusinessType {
|
||||
INDIVIDUAL = "individual",
|
||||
CENTRAL_WORKSHOP = "central_workshop",
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// REQUEST TYPES
|
||||
// ================================================================
|
||||
|
||||
export interface ForecastRequest {
|
||||
inventory_product_id: string;
|
||||
forecast_date: string; // ISO date string
|
||||
forecast_days?: number; // Default: 1, Min: 1, Max: 30
|
||||
location: string;
|
||||
confidence_level?: number; // Default: 0.8, Min: 0.5, Max: 0.95
|
||||
}
|
||||
|
||||
export interface BatchForecastRequest {
|
||||
tenant_id: string;
|
||||
batch_name: string;
|
||||
inventory_product_ids: string[];
|
||||
forecast_days?: number; // Default: 7, Min: 1, Max: 30
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// RESPONSE TYPES
|
||||
// ================================================================
|
||||
|
||||
export interface ForecastResponse {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
inventory_product_id: string;
|
||||
location: string;
|
||||
forecast_date: string; // ISO datetime string
|
||||
|
||||
// Predictions
|
||||
predicted_demand: number;
|
||||
confidence_lower: number;
|
||||
confidence_upper: number;
|
||||
confidence_level: number;
|
||||
|
||||
// Model info
|
||||
model_id: string;
|
||||
model_version: string;
|
||||
algorithm: string;
|
||||
|
||||
// Context
|
||||
business_type: string;
|
||||
is_holiday: boolean;
|
||||
is_weekend: boolean;
|
||||
day_of_week: number;
|
||||
|
||||
// External factors
|
||||
weather_temperature?: number;
|
||||
weather_precipitation?: number;
|
||||
weather_description?: string;
|
||||
traffic_volume?: number;
|
||||
|
||||
// Metadata
|
||||
created_at: string; // ISO datetime string
|
||||
processing_time_ms?: number;
|
||||
features_used?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface BatchForecastResponse {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
batch_name: string;
|
||||
status: string;
|
||||
total_products: number;
|
||||
completed_products: number;
|
||||
failed_products: number;
|
||||
|
||||
// Timing
|
||||
requested_at: string; // ISO datetime string
|
||||
completed_at?: string; // ISO datetime string
|
||||
processing_time_ms?: number;
|
||||
|
||||
// Results
|
||||
forecasts?: ForecastResponse[];
|
||||
error_message?: string;
|
||||
}
|
||||
|
||||
export interface ForecastStatistics {
|
||||
tenant_id: string;
|
||||
total_forecasts: number;
|
||||
recent_forecasts: number;
|
||||
accuracy_metrics: {
|
||||
average_accuracy: number;
|
||||
accuracy_trend: number;
|
||||
};
|
||||
model_performance: {
|
||||
most_used_algorithm: string;
|
||||
average_processing_time: number;
|
||||
};
|
||||
enhanced_features: boolean;
|
||||
repository_integration: boolean;
|
||||
}
|
||||
|
||||
export interface ForecastListResponse {
|
||||
tenant_id: string;
|
||||
forecasts: ForecastResponse[];
|
||||
total_returned: number;
|
||||
filters: {
|
||||
inventory_product_id?: string;
|
||||
start_date?: string; // ISO date string
|
||||
end_date?: string; // ISO date string
|
||||
};
|
||||
pagination: {
|
||||
skip: number;
|
||||
limit: number;
|
||||
};
|
||||
enhanced_features: boolean;
|
||||
repository_integration: boolean;
|
||||
}
|
||||
|
||||
export interface ForecastByIdResponse extends ForecastResponse {
|
||||
enhanced_features: boolean;
|
||||
repository_integration: boolean;
|
||||
}
|
||||
|
||||
export interface DeleteForecastResponse {
|
||||
message: string;
|
||||
forecast_id: string;
|
||||
enhanced_features: boolean;
|
||||
repository_integration: boolean;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// QUERY PARAMETERS
|
||||
// ================================================================
|
||||
|
||||
export interface GetForecastsParams {
|
||||
inventory_product_id?: string;
|
||||
start_date?: string; // ISO date string
|
||||
end_date?: string; // ISO date string
|
||||
skip?: number; // Default: 0
|
||||
limit?: number; // Default: 100
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// HEALTH CHECK
|
||||
// ================================================================
|
||||
|
||||
export interface ForecastingHealthResponse {
|
||||
status: string;
|
||||
service: string;
|
||||
version: string;
|
||||
features: string[];
|
||||
timestamp: string;
|
||||
}
|
||||
Reference in New Issue
Block a user