Files
ember-market-frontend/lib/services/analytics-service.ts
2026-01-10 02:48:24 +00:00

545 lines
13 KiB
TypeScript

"use client";
import { clientFetch } from "../api-client";
// Analytics Types
export interface AnalyticsOverview {
orders: {
total: number;
completed: number;
pending: number;
completionRate: string;
};
revenue: {
total: number;
monthly: number;
weekly: number;
averageOrderValue: number;
};
products: {
total: number;
};
customers: {
unique: number;
};
userType?: "vendor" | "staff";
}
export interface RevenueData {
_id: {
year: number;
month: number;
day: number;
};
revenue: number;
orders: number;
}
export interface ProductPerformance {
productId: string;
name: string;
image: string;
unitType: string;
currentStock: number;
stockStatus: string;
totalSold: number;
totalRevenue: number;
orderCount: number;
averagePrice: number;
}
export interface CustomerInsights {
totalCustomers: number;
segments: {
new: number;
returning: number;
loyal: number;
vip: number;
};
topCustomers: Array<{
_id: string;
orderCount: number;
totalSpent: number;
averageOrderValue: number;
firstOrder: string;
lastOrder: string;
displayName?: string;
username?: string;
}>;
averageOrdersPerCustomer: string;
pagination?: {
currentPage: number;
totalPages: number;
totalCustomers: number;
limit: number;
hasNextPage: boolean;
hasPrevPage: boolean;
startIndex: number;
endIndex: number;
};
}
export interface OrderAnalytics {
statusDistribution: Array<{
_id: string;
count: number;
}>;
}
export interface GrowthAnalytics {
launchDate: string;
generatedAt: string;
daily: Array<{
date: string;
orders: number;
revenue: number;
customers: number;
avgOrderValue: number;
}>;
monthly: Array<{
month: string;
orders: number;
revenue: number;
customers: number;
avgOrderValue: number;
newCustomers: number;
}>;
customers: {
total: number;
segments: {
new: number;
returning: number;
loyal: number;
vip: number;
};
segmentDetails: {
[key: string]: {
count: number;
totalRevenue: number;
avgOrderCount: number;
avgSpent: number;
};
};
segmentPercentages: {
new: number;
returning: number;
loyal: number;
vip: number;
};
};
cumulative: {
orders: number;
revenue: number;
customers: number;
products: number;
avgOrderValue: number;
};
}
// Analytics Service Functions
/**
* Get analytics overview data
* @param storeId Optional storeId for staff users
*/
export const getAnalyticsOverview = async (
storeId?: string,
): Promise<AnalyticsOverview> => {
const url = storeId
? `/analytics/overview?storeId=${storeId}`
: "/analytics/overview";
return clientFetch<AnalyticsOverview>(url);
};
/**
* Get revenue trends data
* @param period Time period in days (7, 30, 90)
* @param storeId Optional storeId for staff users
*/
export const getRevenueTrends = async (
period: string = "30",
storeId?: string,
): Promise<RevenueData[]> => {
const params = new URLSearchParams({ period });
if (storeId) params.append("storeId", storeId);
const url = `/analytics/revenue-trends?${params.toString()}`;
return clientFetch<RevenueData[]>(url);
};
/**
* Get product performance data
* @param storeId Optional storeId for staff users
*/
export const getProductPerformance = async (
storeId?: string,
): Promise<ProductPerformance[]> => {
const url = storeId
? `/analytics/product-performance?storeId=${storeId}`
: "/analytics/product-performance";
return clientFetch<ProductPerformance[]>(url);
};
/**
* Get customer insights data
* @param storeId Optional storeId for staff users
* @param page Page number (default: 1)
* @param limit Number of customers per page (default: 10)
*/
export const getCustomerInsights = async (
storeId?: string,
page: number = 1,
limit: number = 10,
): Promise<CustomerInsights> => {
const params = new URLSearchParams({
page: page.toString(),
limit: limit.toString(),
});
if (storeId) params.append("storeId", storeId);
const url = `/analytics/customer-insights?${params.toString()}`;
return clientFetch<CustomerInsights>(url);
};
/**
* Get order analytics data
* @param period Time period in days (7, 30, 90)
* @param storeId Optional storeId for staff users
*/
export const getOrderAnalytics = async (
period: string = "30",
storeId?: string,
): Promise<OrderAnalytics> => {
const params = new URLSearchParams({ period });
if (storeId) params.append("storeId", storeId);
const url = `/analytics/order-analytics?${params.toString()}`;
return clientFetch<OrderAnalytics>(url);
};
/**
* Get growth analytics data (since first order)
* @param storeId Optional storeId for staff users
*/
export const getGrowthAnalytics = async (
storeId?: string,
): Promise<GrowthAnalytics> => {
const params = new URLSearchParams();
if (storeId) params.append("storeId", storeId);
const url = params.toString()
? `/analytics/growth?${params.toString()}`
: "/analytics/growth";
return clientFetch<GrowthAnalytics>(url);
};
// Helper function to determine if user is staff and get storeId
export const getStoreIdForUser = (): string | undefined => {
if (typeof window === "undefined") return undefined;
// Check if user is staff (you might need to adjust this based on your auth system)
const userType = localStorage.getItem("userType");
const storeId = localStorage.getItem("storeId");
if (userType === "staff" && storeId) {
return storeId;
}
return undefined;
};
// Enhanced analytics functions that automatically handle storeId for staff users
export const getAnalyticsOverviewWithStore =
async (): Promise<AnalyticsOverview> => {
const storeId = getStoreIdForUser();
return getAnalyticsOverview(storeId);
};
export const getRevenueTrendsWithStore = async (
period: string = "30",
): Promise<RevenueData[]> => {
const storeId = getStoreIdForUser();
return getRevenueTrends(period, storeId);
};
export const getProductPerformanceWithStore = async (): Promise<
ProductPerformance[]
> => {
const storeId = getStoreIdForUser();
return getProductPerformance(storeId);
};
export const getCustomerInsightsWithStore = async (
page: number = 1,
limit: number = 10,
): Promise<CustomerInsights> => {
const storeId = getStoreIdForUser();
return getCustomerInsights(storeId, page, limit);
};
export const getOrderAnalyticsWithStore = async (
period: string = "30",
): Promise<OrderAnalytics> => {
const storeId = getStoreIdForUser();
return getOrderAnalytics(period, storeId);
};
export const getGrowthAnalyticsWithStore =
async (): Promise<GrowthAnalytics> => {
const storeId = getStoreIdForUser();
return getGrowthAnalytics(storeId);
};
export function formatGBP(value: number) {
return value.toLocaleString("en-GB", {
style: "currency",
currency: "GBP",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
// Prediction Types
export interface SalesPrediction {
predicted: number | null;
predictedOrders: number | null;
dailyPredictions?: Array<{
day: number;
predicted: number;
date: string;
}>;
confidence: "very_high" | "high" | "medium" | "low";
method: string;
methods?: {
aiNeuralNetwork?: number | null;
weightedMovingAverage?: number | null;
exponentialSmoothing?: number | null;
holtWinters?: number | null;
weightedLinearRegression?: number | null;
trendAdjusted?: number | null;
};
aiModel?: {
used: boolean;
available?: boolean;
confidence?: string;
modelAccuracy?: number;
error?: string | null;
};
trend?: {
direction: "up" | "down" | "neutral";
strength: number;
acceleration: number;
slope?: number;
};
variance?: number;
minPrediction?: number;
maxPrediction?: number;
confidenceScore?: number;
confidenceIntervals?: {
lower: number;
upper: number;
confidenceScore?: number;
modelAgreement?: number;
avgModelAccuracy?: number;
dataConsistency?: number;
};
modelPerformance?: {
[key: string]: {
mae?: number;
mape?: number;
rmse?: number;
accuracy?: number;
confidence?: string;
};
};
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: "very_high" | "high" | "medium" | "low";
averageDaily?: number;
trendFactor?: number;
stdDev?: number;
confidenceIntervals?: {
lower: number;
upper: number;
confidenceScore?: number;
modelAgreement?: number;
avgModelAccuracy?: number;
dataConsistency?: 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: "very_high" | "high" | "medium" | "low";
averageDailySales?: number;
stdDev?: number;
optimisticDays?: number | null;
pessimisticDays?: number | null;
optimisticDate?: string | null;
pessimisticDate?: string | null;
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);
};