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:
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user