Files
ember-market-frontend/lib/services/analytics-service.ts
g ce1d2d3fe8 Add growth analytics chart and service integration
Introduces a new GrowthAnalyticsChart component and integrates growth analytics data into the dashboard. Updates analytics-service to support growth analytics API, adds types, and exposes helper functions for fetching growth analytics with store context. The dashboard UI now includes a 'Growth' tab for visualizing store growth metrics and trends.
2026-01-07 03:57:31 +00:00

311 lines
7.5 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 {
period: {
start: string;
end: string;
days: number;
granularity: "daily" | "weekly" | "monthly";
};
summary: {
currentPeriod: {
revenue: number;
orders: number;
avgOrderValue: number;
customers: number;
};
previousPeriod: {
revenue: number;
orders: number;
avgOrderValue: number;
customers: number;
};
growthRates: {
revenue: number;
orders: number;
avgOrderValue: number;
customers: number;
};
};
customerInsights: {
newCustomers: number;
returningCustomers: number;
totalCustomers: number;
newCustomerRate: number;
avgOrdersPerCustomer: number;
avgSpentPerCustomer: number;
};
timeSeries: Array<{
date: string;
revenue: number;
orders: number;
avgOrderValue: number;
uniqueCustomers: number;
cumulativeRevenue: number;
cumulativeOrders: number;
}>;
topGrowingProducts: Array<{
productId: string;
productName: string;
currentPeriodRevenue: number;
previousPeriodRevenue: number;
revenueGrowth: number;
currentPeriodQuantity: number;
previousPeriodQuantity: 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
* @param period Time period: "7", "30", "90", "365", or "all" (default: "30")
* @param granularity Data granularity: "daily", "weekly", "monthly" (auto-selected if not specified)
* @param storeId Optional storeId for staff users
*/
export const getGrowthAnalytics = async (
period: string = "30",
granularity?: string,
storeId?: string,
): Promise<GrowthAnalytics> => {
const params = new URLSearchParams({ period });
if (granularity) params.append("granularity", granularity);
if (storeId) params.append("storeId", storeId);
const url = `/analytics/growth?${params.toString()}`;
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 (
period: string = "30",
granularity?: string,
): Promise<GrowthAnalytics> => {
const storeId = getStoreIdForUser();
return getGrowthAnalytics(period, granularity, storeId);
};
export function formatGBP(value: number) {
return value.toLocaleString("en-GB", {
style: "currency",
currency: "GBP",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}