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.
This commit is contained in:
g
2026-01-07 03:57:31 +00:00
parent 8862aecc61
commit ce1d2d3fe8
3 changed files with 789 additions and 107 deletions

View File

@@ -1,6 +1,6 @@
'use client';
"use client";
import { clientFetch } from '../api-client';
import { clientFetch } from "../api-client";
// Analytics Types
export interface AnalyticsOverview {
@@ -22,7 +22,7 @@ export interface AnalyticsOverview {
customers: {
unique: number;
};
userType?: 'vendor' | 'staff';
userType?: "vendor" | "staff";
}
export interface RevenueData {
@@ -86,14 +86,73 @@ export interface OrderAnalytics {
}>;
}
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';
export const getAnalyticsOverview = async (
storeId?: string,
): Promise<AnalyticsOverview> => {
const url = storeId
? `/analytics/overview?storeId=${storeId}`
: "/analytics/overview";
return clientFetch<AnalyticsOverview>(url);
};
@@ -102,10 +161,13 @@ export const getAnalyticsOverview = async (storeId?: string): Promise<AnalyticsO
* @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[]> => {
export const getRevenueTrends = async (
period: string = "30",
storeId?: string,
): Promise<RevenueData[]> => {
const params = new URLSearchParams({ period });
if (storeId) params.append('storeId', storeId);
if (storeId) params.append("storeId", storeId);
const url = `/analytics/revenue-trends?${params.toString()}`;
return clientFetch<RevenueData[]>(url);
};
@@ -114,8 +176,12 @@ export const getRevenueTrends = async (period: string = '30', storeId?: string):
* 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';
export const getProductPerformance = async (
storeId?: string,
): Promise<ProductPerformance[]> => {
const url = storeId
? `/analytics/product-performance?storeId=${storeId}`
: "/analytics/product-performance";
return clientFetch<ProductPerformance[]>(url);
};
@@ -125,13 +191,17 @@ export const getProductPerformance = async (storeId?: string): Promise<ProductPe
* @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()
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);
if (storeId) params.append("storeId", storeId);
const url = `/analytics/customer-insights?${params.toString()}`;
return clientFetch<CustomerInsights>(url);
};
@@ -141,60 +211,100 @@ export const getCustomerInsights = async (storeId?: string, page: number = 1, li
* @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> => {
export const getOrderAnalytics = async (
period: string = "30",
storeId?: string,
): Promise<OrderAnalytics> => {
const params = new URLSearchParams({ period });
if (storeId) params.append('storeId', storeId);
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;
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) {
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 getAnalyticsOverviewWithStore =
async (): Promise<AnalyticsOverview> => {
const storeId = getStoreIdForUser();
return getAnalyticsOverview(storeId);
};
export const getRevenueTrendsWithStore = async (period: string = '30'): Promise<RevenueData[]> => {
export const getRevenueTrendsWithStore = async (
period: string = "30",
): Promise<RevenueData[]> => {
const storeId = getStoreIdForUser();
return getRevenueTrends(period, storeId);
};
export const getProductPerformanceWithStore = async (): Promise<ProductPerformance[]> => {
export const getProductPerformanceWithStore = async (): Promise<
ProductPerformance[]
> => {
const storeId = getStoreIdForUser();
return getProductPerformance(storeId);
};
export const getCustomerInsightsWithStore = async (page: number = 1, limit: number = 10): Promise<CustomerInsights> => {
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> => {
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',
return value.toLocaleString("en-GB", {
style: "currency",
currency: "GBP",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
}