diff --git a/components/analytics/CustomerInsightsChart.tsx b/components/analytics/CustomerInsightsChart.tsx index 715bbe1..04830b4 100644 --- a/components/analytics/CustomerInsightsChart.tsx +++ b/components/analytics/CustomerInsightsChart.tsx @@ -3,9 +3,11 @@ import { useState, useEffect } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useToast } from "@/hooks/use-toast"; import { Skeleton } from "@/components/ui/skeleton"; -import { Users, Crown, UserPlus, UserCheck, Star } from "lucide-react"; +import { Users, Crown, UserPlus, UserCheck, Star, ChevronLeft, ChevronRight } from "lucide-react"; import { getCustomerInsightsWithStore, type CustomerInsights } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; import { CustomerInsightsSkeleton } from './SkeletonLoaders'; @@ -14,6 +16,8 @@ export default function CustomerInsightsChart() { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); + const [currentPage, setCurrentPage] = useState(1); + const [pageSize, setPageSize] = useState(10); const { toast } = useToast(); useEffect(() => { @@ -21,7 +25,7 @@ export default function CustomerInsightsChart() { try { setIsLoading(true); setError(null); - const response = await getCustomerInsightsWithStore(); + const response = await getCustomerInsightsWithStore(currentPage, pageSize); setData(response); } catch (err) { console.error('Error fetching customer insights:', err); @@ -37,7 +41,7 @@ export default function CustomerInsightsChart() { }; fetchCustomerData(); - }, [toast]); + }, [toast, currentPage, pageSize]); const getSegmentColor = (segment: string) => { switch (segment) { @@ -116,6 +120,15 @@ export default function CustomerInsightsChart() { const segments = Object.entries(data.segments); const totalCustomers = data.totalCustomers; + const handlePageChange = (newPage: number) => { + setCurrentPage(newPage); + }; + + const handlePageSizeChange = (newPageSize: string) => { + setPageSize(parseInt(newPageSize)); + setCurrentPage(1); // Reset to first page when changing page size + }; + return (
{/* Customer Overview */} @@ -194,6 +207,11 @@ export default function CustomerInsightsChart() { Your highest-spending customers + {data.pagination && ( + + (Showing {data.pagination.startIndex}-{data.pagination.endIndex} of {data.pagination.totalCustomers}) + + )} @@ -203,31 +221,85 @@ export default function CustomerInsightsChart() {

No customer data available

) : ( -
- {data.topCustomers.map((customer, index) => ( -
-
-
- {index + 1} -
-
-
{customer.displayName || `Customer #${customer._id.slice(-6)}`}
-
- {customer.orderCount} orders + <> +
+ {data.topCustomers.map((customer, index) => { + const globalRank = data.pagination ? (data.pagination.currentPage - 1) * data.pagination.limit + index + 1 : index + 1; + return ( +
+
+
+ {globalRank} +
+
+
{customer.displayName || `Customer #${customer._id.slice(-6)}`}
+
+ {customer.orderCount} orders +
+
+
+
+
+ {formatGBP(customer.totalSpent)} +
+
+ {formatGBP(customer.averageOrderValue)} avg +
+ ); + })} +
+ + {/* Pagination Controls */} + {data.pagination && data.pagination.totalPages > 1 && ( +
+
+ Show + + customers per page
-
-
- {formatGBP(customer.totalSpent)} -
-
- {formatGBP(customer.averageOrderValue)} avg + +
+ + +
+ + Page {data.pagination.currentPage} of {data.pagination.totalPages} +
+ +
- ))} -
+ )} + )} diff --git a/lib/services/analytics-service.ts b/lib/services/analytics-service.ts index eca1e5c..e9bb4ac 100644 --- a/lib/services/analytics-service.ts +++ b/lib/services/analytics-service.ts @@ -67,6 +67,16 @@ export interface CustomerInsights { username?: string; }>; averageOrdersPerCustomer: string; + pagination?: { + currentPage: number; + totalPages: number; + totalCustomers: number; + limit: number; + hasNextPage: boolean; + hasPrevPage: boolean; + startIndex: number; + endIndex: number; + }; } export interface OrderAnalytics { @@ -112,9 +122,17 @@ export const getProductPerformance = async (storeId?: string): Promise => { - const url = storeId ? `/analytics/customer-insights?storeId=${storeId}` : '/analytics/customer-insights'; +export const getCustomerInsights = async (storeId?: string, page: number = 1, limit: number = 10): Promise => { + 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(url); }; @@ -162,9 +180,9 @@ export const getProductPerformanceWithStore = async (): Promise => { +export const getCustomerInsightsWithStore = async (page: number = 1, limit: number = 10): Promise => { const storeId = getStoreIdForUser(); - return getCustomerInsights(storeId); + return getCustomerInsights(storeId, page, limit); }; export const getOrderAnalyticsWithStore = async (period: string = '30'): Promise => { diff --git a/public/git-info.json b/public/git-info.json index eca2f83..de113eb 100644 --- a/public/git-info.json +++ b/public/git-info.json @@ -1,4 +1,4 @@ { - "commitHash": "6a2cd9a", - "buildTime": "2025-08-26T20:09:00.025Z" + "commitHash": "57502d0", + "buildTime": "2025-08-30T20:06:32.862Z" } \ No newline at end of file diff --git a/public/loaderio-4f43d28a66724ee6c044221219487237.txt b/public/loaderio-4f43d28a66724ee6c044221219487237.txt index 23ac577..19b1ab9 100644 --- a/public/loaderio-4f43d28a66724ee6c044221219487237.txt +++ b/public/loaderio-4f43d28a66724ee6c044221219487237.txt @@ -1 +1,2 @@ loaderio-4f43d28a66724ee6c044221219487237 +