Add predictions and forecasting analytics

Introduces a new PredictionsChart component and integrates it into the AnalyticsDashboard with a new tab for AI-powered sales, demand, and stock forecasting. Expands analytics-service with types and service functions for predictions, enabling comprehensive future insights for revenue, demand, and inventory.
This commit is contained in:
g
2026-01-10 01:47:38 +00:00
parent 1cad96887e
commit c69367e6da
3 changed files with 642 additions and 1 deletions

View File

@@ -298,3 +298,202 @@ export function formatGBP(value: number) {
maximumFractionDigits: 2,
});
}
// Prediction Types
export interface SalesPrediction {
predicted: number | null;
predictedOrders: number | null;
dailyPredictions?: Array<{
day: number;
predicted: number;
date: string;
}>;
confidence: "high" | "medium" | "low";
method: string;
methods?: {
movingAverage?: number | null;
exponentialAverage?: number | null;
linearRegression?: number | null;
trendAverage?: number | null;
};
variance?: number;
minPrediction?: number;
maxPrediction?: number;
seasonality?: {
dayOfWeek: Record<string, number>;
month: Record<string, number>;
confidence: string;
};
historicalPeriod: number;
predictionPeriod: number;
message?: string;
}
export interface DemandPrediction {
predictedDaily: number | null;
predictedWeekly: number | null;
predictedMonthly: number | null;
confidence: "high" | "medium" | "low";
averageDaily?: number;
trendFactor?: number;
stdDev?: number;
historicalPeriod: number;
predictionPeriod: number;
productId?: string | null;
message?: string;
}
export interface StockPrediction {
productId: string;
productName: string;
currentStock: number;
lowStockThreshold: number;
unitType: string;
prediction: {
daysUntilOutOfStock: number | null;
estimatedDate: string | null;
confidence: "high" | "medium" | "low";
averageDailySales?: number;
stdDev?: number;
message?: string;
};
needsRestock: boolean;
}
export interface StockPredictionsResponse {
predictions: StockPrediction[];
historicalPeriod: number;
totalProducts: number;
productsNeedingRestock: number;
}
export interface PredictionsOverview {
sales: SalesPrediction;
demand: DemandPrediction;
stock: {
totalProducts: number;
message?: string;
};
historicalPeriod: number;
predictionPeriod: number;
message?: string;
}
// Prediction Service Functions
/**
* Get sales/revenue predictions
* @param daysAhead Number of days to predict ahead (default: 7)
* @param period Historical period in days (default: 30)
* @param storeId Optional storeId for staff users
*/
export const getSalesPredictions = async (
daysAhead: number = 7,
period: number = 30,
storeId?: string,
): Promise<SalesPrediction> => {
const params = new URLSearchParams({
daysAhead: daysAhead.toString(),
period: period.toString(),
});
if (storeId) params.append("storeId", storeId);
const url = `/analytics/predictions/sales?${params.toString()}`;
return clientFetch<SalesPrediction>(url);
};
/**
* Get product demand predictions
* @param productId Optional product ID for specific product prediction
* @param daysAhead Number of days to predict ahead (default: 7)
* @param period Historical period in days (default: 30)
* @param storeId Optional storeId for staff users
*/
export const getDemandPredictions = async (
productId?: string,
daysAhead: number = 7,
period: number = 30,
storeId?: string,
): Promise<DemandPrediction> => {
const params = new URLSearchParams({
daysAhead: daysAhead.toString(),
period: period.toString(),
});
if (productId) params.append("productId", productId);
if (storeId) params.append("storeId", storeId);
const url = `/analytics/predictions/demand?${params.toString()}`;
return clientFetch<DemandPrediction>(url);
};
/**
* Get stock depletion predictions
* @param period Historical period in days (default: 30)
* @param storeId Optional storeId for staff users
*/
export const getStockPredictions = async (
period: number = 30,
storeId?: string,
): Promise<StockPredictionsResponse> => {
const params = new URLSearchParams({
period: period.toString(),
});
if (storeId) params.append("storeId", storeId);
const url = `/analytics/predictions/stock?${params.toString()}`;
return clientFetch<StockPredictionsResponse>(url);
};
/**
* Get comprehensive predictions overview
* @param daysAhead Number of days to predict ahead (default: 7)
* @param period Historical period in days (default: 30)
* @param storeId Optional storeId for staff users
*/
export const getPredictionsOverview = async (
daysAhead: number = 7,
period: number = 30,
storeId?: string,
): Promise<PredictionsOverview> => {
const params = new URLSearchParams({
daysAhead: daysAhead.toString(),
period: period.toString(),
});
if (storeId) params.append("storeId", storeId);
const url = `/analytics/predictions/overview?${params.toString()}`;
return clientFetch<PredictionsOverview>(url);
};
// Helper functions with automatic storeId handling
export const getSalesPredictionsWithStore = async (
daysAhead: number = 7,
period: number = 30,
): Promise<SalesPrediction> => {
const storeId = getStoreIdForUser();
return getSalesPredictions(daysAhead, period, storeId);
};
export const getDemandPredictionsWithStore = async (
productId?: string,
daysAhead: number = 7,
period: number = 30,
): Promise<DemandPrediction> => {
const storeId = getStoreIdForUser();
return getDemandPredictions(productId, daysAhead, period, storeId);
};
export const getStockPredictionsWithStore = async (
period: number = 30,
): Promise<StockPredictionsResponse> => {
const storeId = getStoreIdForUser();
return getStockPredictions(period, storeId);
};
export const getPredictionsOverviewWithStore = async (
daysAhead: number = 7,
period: number = 30,
): Promise<PredictionsOverview> => {
const storeId = getStoreIdForUser();
return getPredictionsOverview(daysAhead, period, storeId);
};